diff options
author | Daniel Wilhelm <shieldwed@outlook.com> | 2017-01-09 10:54:11 +0100 |
---|---|---|
committer | Daniel Wilhelm <shieldwed@outlook.com> | 2017-01-09 10:54:11 +0100 |
commit | 6011596a2f247bde24095e985f6e2547e4b191ef (patch) | |
tree | 296003a2c8952f41b2998653ba02911ba2b83b0a | |
parent | 8.7 (diff) | |
download | FreeFileSync-6011596a2f247bde24095e985f6e2547e4b191ef.tar.gz FreeFileSync-6011596a2f247bde24095e985f6e2547e4b191ef.tar.bz2 FreeFileSync-6011596a2f247bde24095e985f6e2547e4b191ef.zip |
8.8
59 files changed, 1819 insertions, 1339 deletions
diff --git a/FreeFileSync/Build/Changelog.txt b/FreeFileSync/Build/Changelog.txt index 2831c26d..7bffa391 100644 --- a/FreeFileSync/Build/Changelog.txt +++ b/FreeFileSync/Build/Changelog.txt @@ -1,3 +1,20 @@ +FreeFileSync 8.8 [2017-01-08] +----------------------------- +Distinguish file access failure from not existing during sync +Further optimized number of file I/O operations via file system abstraction +Report unexpected prompts for keyboard-interactive SFTP authentication +Mark followed directory symlinks on grid +Fixed parent path determination for UNC +Don't skip source files that cannot be accessed +Don't consider a symlink type for SFTP when comparing by content +Fixed invalid parameter error when setting file times on exFAT file system +Don't allow overwriting folder with equally named file when copying from main dialog +Fixed failure to create intermediate directories for Cryptomator/Webdav +Refactored file system abstraction layer for future FTP support +Fixed failure to change file name case on MTP devices +Fixed late failure for batch recycling when parsing of single item fails + + FreeFileSync 8.7 [2016-12-06] ----------------------------- New auto-updater feature for FreeFileSync Donation Edition diff --git a/FreeFileSync/Build/Languages/dutch.lng b/FreeFileSync/Build/Languages/dutch.lng index 80b2e795..ff73514a 100644 --- a/FreeFileSync/Build/Languages/dutch.lng +++ b/FreeFileSync/Build/Languages/dutch.lng @@ -603,7 +603,7 @@ De opdracht wordt geactiveerd als: <target>Verwijderen van oude versies...</target> <source>Updating file %x</source> -<target>File %x bijwerken</target> +<target>Bestand %x bijwerken</target> <source>Updating symbolic link %x</source> <target>Symbolische link %x bijwerken</target> diff --git a/FreeFileSync/Build/Languages/german.lng b/FreeFileSync/Build/Languages/german.lng index a3cff38c..803b0bd3 100644 --- a/FreeFileSync/Build/Languages/german.lng +++ b/FreeFileSync/Build/Languages/german.lng @@ -7,11 +7,8 @@ <plural_definition>n == 1 ? 0 : 1</plural_definition> </header> -<source>Installation was registered on a different operating system.</source> -<target>Die Installation wurde auf einem anderen Betriebssystem registriert.</target> - -<source>The server does not support authentication via %x.</source> -<target>Der Server unterstützt keine Authentifizierung über %x.</target> +<source>Cannot delete symbolic link %x.</source> +<target>Die symbolischen Verknüpfung %x kann nicht gelöscht werden.</target> <source>Both sides have changed since last synchronization.</source> <target>Beide Seiten wurden seit der letzten Synchronisation verändert.</target> @@ -28,12 +25,12 @@ <source>Setting default synchronization directions: Old files will be overwritten with newer files.</source> <target>Setze Standardwerte für Synchronisationsrichtungen: Alte Dateien werden durch neuere überschrieben.</target> -<source>Creating folder %x</source> -<target>Erstelle Ordner %x</target> - <source>Creating file %x</source> <target>Erstelle Datei %x</target> +<source>Creating folder %x</source> +<target>Erstelle Ordner %x</target> + <source>Creating symbolic link %x</source> <target>Erstelle symbolische Verknüpfung %x</target> @@ -288,30 +285,33 @@ Tatsächlich: %y bytes <source>Cannot read directory %x.</source> <target>Das Verzeichnis %x kann nicht gelesen werden.</target> +<source>Cannot read file %x.</source> +<target>Die Datei %x kann nicht gelesen werden.</target> + <source>Cannot read file attributes of %x.</source> <target>Die Dateiattribute von %x können nicht gelesen werden.</target> -<source>Cannot find %x.</source> -<target>%x wurde nicht gefunden.</target> - <source>Cannot open file %x.</source> <target>Die Datei %x kann nicht geöffnet werden.</target> -<source>Cannot read file %x.</source> -<target>Die Datei %x kann nicht gelesen werden.</target> - <source>Cannot find device %x.</source> <target>Das Gerät %x wurde nicht gefunden.</target> +<source>Cannot find %x.</source> +<target>%x wurde nicht gefunden.</target> + +<source>Type of item %x is not supported:</source> +<target>Der Typ des Elements %x wird nicht unterstützt:</target> + <source>Cannot create directory %x.</source> <target>Das Verzeichnis %x kann nicht erstellt werden.</target> -<source>Cannot delete directory %x.</source> -<target>Das Verzeichnis %x kann nicht gelöscht werden.</target> - <source>Cannot delete file %x.</source> <target>Die Datei %x kann nicht gelöscht werden.</target> +<source>Cannot delete directory %x.</source> +<target>Das Verzeichnis %x kann nicht gelöscht werden.</target> + <source>Cannot write modification time of %x.</source> <target>Die Änderungszeit von %x kann nicht geschrieben werden.</target> @@ -330,6 +330,12 @@ Tatsächlich: %y bytes <source>Incorrect command line:</source> <target>Ungültige Befehlszeile:</target> +<source>The server does not support authentication via %x.</source> +<target>Der Server unterstützt keine Authentifizierung über %x.</target> + +<source>Required:</source> +<target>Benötigt:</target> + <source>Unable to access %x.</source> <target>Auf %x kann nicht zugegriffen werden.</target> @@ -677,9 +683,6 @@ Die Befehlszeile wird ausgelöst, wenn: <source>Not enough free disk space available in:</source> <target>Nicht genügend freier Speicher verfügbar für:</target> -<source>Required:</source> -<target>Benötigt:</target> - <source>Available:</source> <target>Verfügbar:</target> @@ -1777,6 +1780,9 @@ Dadurch wird ein konsistenter Datenstand auch bei schweren Fehlern garantiert. <source>Consistency check failed for %x.</source> <target>Die Konsistenzprüfung für %x ist fehlgeschlagen.</target> +<source>Installation was registered on a different operating system.</source> +<target>Die Installation wurde auf einem anderen Betriebssystem registriert.</target> + <source>Failed to activate FreeFileSync Donation Edition.</source> <target>Die Aktivierung der FreeFileSync Spendenversion ist fehlgeschlagen.</target> @@ -1819,9 +1825,6 @@ Dadurch wird ein konsistenter Datenstand auch bei schweren Fehlern garantiert. <source>Cannot copy attributes from %x to %y.</source> <target>Die Attribute können nicht von %x nach %y kopiert werden.</target> -<source>Type of item %x is not supported:</source> -<target>Der Typ des Elements %x wird nicht unterstützt:</target> - <source>%x TB</source> <target>%x TB</target> diff --git a/FreeFileSync/Source/RealTimeSync/application.cpp b/FreeFileSync/Source/RealTimeSync/application.cpp index cc127249..a6edef0e 100644 --- a/FreeFileSync/Source/RealTimeSync/application.cpp +++ b/FreeFileSync/Source/RealTimeSync/application.cpp @@ -119,11 +119,11 @@ void Application::onEnterEventLoop(wxEvent& event) { Zstring filePath = getResolvedFilePath(toZ(argv[i])); - if (!fileExists(filePath)) //be a little tolerant + if (!fileAvailable(filePath)) //be a little tolerant { - if (fileExists(filePath + Zstr(".ffs_real"))) + if (fileAvailable(filePath + Zstr(".ffs_real"))) filePath += Zstr(".ffs_real"); - else if (fileExists(filePath + Zstr(".ffs_batch"))) + else if (fileAvailable(filePath + Zstr(".ffs_batch"))) filePath += Zstr(".ffs_batch"); else { diff --git a/FreeFileSync/Source/RealTimeSync/grep b/FreeFileSync/Source/RealTimeSync/grep new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/FreeFileSync/Source/RealTimeSync/grep diff --git a/FreeFileSync/Source/RealTimeSync/gui_generated.cpp b/FreeFileSync/Source/RealTimeSync/gui_generated.cpp index 1b025037..cc5dbb99 100644 --- a/FreeFileSync/Source/RealTimeSync/gui_generated.cpp +++ b/FreeFileSync/Source/RealTimeSync/gui_generated.cpp @@ -13,7 +13,7 @@ MainDlgGenerated::MainDlgGenerated( wxWindow* parent, wxWindowID id, const wxString& title, const wxPoint& pos, const wxSize& size, long style ) : wxFrame( parent, id, title, pos, size, style ) { - this->SetSizeHints( wxSize( -1,-1 ), wxDefaultSize ); + this->SetSizeHints( wxSize( -1, -1 ), wxDefaultSize ); this->SetBackgroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_BTNFACE ) ); m_menubar1 = new wxMenuBar( 0 ); @@ -23,13 +23,13 @@ MainDlgGenerated::MainDlgGenerated( wxWindow* parent, wxWindowID id, const wxStr m_menuFile->Append( m_menuItem13 ); wxMenuItem* m_menuItem14; - m_menuItem14 = new wxMenuItem( m_menuFile, wxID_SAVEAS, wxString( _("Save &as...") ) , wxEmptyString, wxITEM_NORMAL ); + m_menuItem14 = new wxMenuItem( m_menuFile, wxID_SAVEAS, wxString( _("Save &as...") ), wxEmptyString, wxITEM_NORMAL ); m_menuFile->Append( m_menuItem14 ); m_menuFile->AppendSeparator(); wxMenuItem* m_menuItem4; - m_menuItem4 = new wxMenuItem( m_menuFile, wxID_EXIT, wxString( _("E&xit") ) , wxEmptyString, wxITEM_NORMAL ); + m_menuItem4 = new wxMenuItem( m_menuFile, wxID_EXIT, wxString( _("E&xit") ), wxEmptyString, wxITEM_NORMAL ); m_menuFile->Append( m_menuItem4 ); m_menubar1->Append( m_menuFile, _("&File") ); @@ -127,12 +127,12 @@ MainDlgGenerated::MainDlgGenerated( wxWindow* parent, wxWindowID id, const wxStr wxBoxSizer* bSizer20; bSizer20 = new wxBoxSizer( wxHORIZONTAL ); - m_bpButtonAddFolder = new wxBitmapButton( m_panelMainFolder, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 25,25 ), wxBU_AUTODRAW ); + m_bpButtonAddFolder = new wxBitmapButton( m_panelMainFolder, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 25, 25 ), wxBU_AUTODRAW ); m_bpButtonAddFolder->SetToolTip( _("Add folder") ); bSizer20->Add( m_bpButtonAddFolder, 0, wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 5 ); - m_bpButtonRemoveTopFolder = new wxBitmapButton( m_panelMainFolder, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 25,25 ), wxBU_AUTODRAW ); + m_bpButtonRemoveTopFolder = new wxBitmapButton( m_panelMainFolder, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 25, 25 ), wxBU_AUTODRAW ); m_bpButtonRemoveTopFolder->SetToolTip( _("Remove folder") ); bSizer20->Add( m_bpButtonRemoveTopFolder, 0, wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 5 ); @@ -143,7 +143,7 @@ MainDlgGenerated::MainDlgGenerated( wxWindow* parent, wxWindowID id, const wxStr wxBoxSizer* bSizer19; bSizer19 = new wxBoxSizer( wxHORIZONTAL ); - m_txtCtrlDirectoryMain = new wxTextCtrl( m_panelMainFolder, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize( 300,-1 ), 0 ); + m_txtCtrlDirectoryMain = new wxTextCtrl( m_panelMainFolder, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize( 300, -1 ), 0 ); m_txtCtrlDirectoryMain->SetMaxLength( 0 ); bSizer19->Add( m_txtCtrlDirectoryMain, 1, wxALIGN_CENTER_VERTICAL, 5 ); @@ -222,7 +222,7 @@ MainDlgGenerated::MainDlgGenerated( wxWindow* parent, wxWindowID id, const wxStr m_staticline5 = new wxStaticLine( this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxLI_HORIZONTAL ); bSizerMain->Add( m_staticline5, 0, wxEXPAND, 5 ); - m_buttonStart = new zen::BitmapTextButton( this, wxID_OK, _("Start"), wxDefaultPosition, wxSize( -1,-1 ), 0 ); + m_buttonStart = new zen::BitmapTextButton( this, wxID_OK, _("Start"), wxDefaultPosition, wxSize( -1, -1 ), 0 ); m_buttonStart->SetDefault(); m_buttonStart->SetFont( wxFont( wxNORMAL_FONT->GetPointSize(), 70, 90, 92, false, wxEmptyString ) ); @@ -258,7 +258,7 @@ FolderGenerated::FolderGenerated( wxWindow* parent, wxWindowID id, const wxPoint wxBoxSizer* bSizer114; bSizer114 = new wxBoxSizer( wxHORIZONTAL ); - m_bpButtonRemoveFolder = new wxBitmapButton( this, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 25,25 ), wxBU_AUTODRAW ); + m_bpButtonRemoveFolder = new wxBitmapButton( this, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 25, 25 ), wxBU_AUTODRAW ); m_bpButtonRemoveFolder->SetToolTip( _("Remove folder") ); bSizer114->Add( m_bpButtonRemoveFolder, 0, wxALIGN_CENTER_VERTICAL, 5 ); diff --git a/FreeFileSync/Source/RealTimeSync/gui_generated.h b/FreeFileSync/Source/RealTimeSync/gui_generated.h index f3018d48..b16f1a97 100644 --- a/FreeFileSync/Source/RealTimeSync/gui_generated.h +++ b/FreeFileSync/Source/RealTimeSync/gui_generated.h @@ -89,7 +89,7 @@ protected: public: - MainDlgGenerated( wxWindow* parent, wxWindowID id = wxID_ANY, const wxString& title = _("dummy"), const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxSize( -1,-1 ), long style = wxDEFAULT_FRAME_STYLE|wxTAB_TRAVERSAL ); + MainDlgGenerated( wxWindow* parent, wxWindowID id = wxID_ANY, const wxString& title = _("dummy"), const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxSize( -1, -1 ), long style = wxDEFAULT_FRAME_STYLE|wxTAB_TRAVERSAL ); ~MainDlgGenerated(); @@ -109,7 +109,7 @@ public: wxBitmapButton* m_bpButtonRemoveFolder; wxTextCtrl* m_txtCtrlDirectory; - FolderGenerated( wxWindow* parent, wxWindowID id = wxID_ANY, const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxSize( -1,-1 ), long style = 0 ); + FolderGenerated( wxWindow* parent, wxWindowID id = wxID_ANY, const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxSize( -1, -1 ), long style = 0 ); ~FolderGenerated(); }; diff --git a/FreeFileSync/Source/RealTimeSync/main_dlg.cpp b/FreeFileSync/Source/RealTimeSync/main_dlg.cpp index 4bb61add..61c7d255 100644 --- a/FreeFileSync/Source/RealTimeSync/main_dlg.cpp +++ b/FreeFileSync/Source/RealTimeSync/main_dlg.cpp @@ -100,7 +100,7 @@ MainDialog::MainDialog(wxDialog* dlg, const Zstring& cfgFileName) //--------------------------- load config values ------------------------------------ xmlAccess::XmlRealConfig newConfig; - const Zstring currentConfigFile = cfgFileName.empty() ? lastRunConfigPath_ : cfgFileName; + const Zstring currentConfigFile = !cfgFileName.empty() ? cfgFileName : lastRunConfigPath_; bool loadCfgSuccess = false; if (!cfgFileName.empty() || fileExists(lastRunConfigPath_)) try @@ -324,7 +324,7 @@ void MainDialog::OnConfigLoad(wxCommandEvent& event) wxFileDialog filePicker(this, wxString(), utfCvrtTo<wxString>(beforeLast(currentConfigFileName_, FILE_NAME_SEPARATOR, IF_MISSING_RETURN_NONE)), //default dir - wxString(), + wxString(), //default file 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/monitor.cpp b/FreeFileSync/Source/RealTimeSync/monitor.cpp index a7902eb6..87c7e5e1 100644 --- a/FreeFileSync/Source/RealTimeSync/monitor.cpp +++ b/FreeFileSync/Source/RealTimeSync/monitor.cpp @@ -50,46 +50,47 @@ struct WaitResult enum ChangeType { CHANGE_DETECTED, - CHANGE_DIR_MISSING + CHANGE_DIR_UNAVAILABLE //not existing or can't access }; WaitResult(const zen::DirWatcher::Entry& changedItem) : type(CHANGE_DETECTED), changedItem_(changedItem) {} - WaitResult(const Zstring& folderPath) : type(CHANGE_DIR_MISSING), folderPath_(folderPath) {} + WaitResult(const Zstring& folderPath) : type(CHANGE_DIR_UNAVAILABLE), folderPath_(folderPath) {} ChangeType type; zen::DirWatcher::Entry changedItem_; //for type == CHANGE_DETECTED: file or directory - Zstring folderPath_; //for type == CHANGE_DIR_MISSING + Zstring folderPath_; //for type == CHANGE_DIR_UNAVAILABLE }; WaitResult waitForChanges(const std::vector<Zstring>& folderPathPhrases, //throw FileError const std::function<void(bool readyForSync)>& onRefreshGui) { - const std::vector<Zstring> folderPathsFmt = getFormattedDirs(folderPathPhrases); //throw FileError - if (folderPathsFmt.empty()) //pathological case, but we have to check else this function will wait endlessly + const std::vector<Zstring> folderPaths = getFormattedDirs(folderPathPhrases); //throw FileError + if (folderPaths.empty()) //pathological case, but we have to check else this function will wait endlessly throw FileError(_("A folder input field is empty.")); //should have been checked by caller! //detect when volumes are removed/are not available anymore std::vector<std::pair<Zstring, std::shared_ptr<DirWatcher>>> watches; - for (const Zstring& folderPathFmt : folderPathsFmt) + for (const Zstring& folderPath : folderPaths) { try { //a non-existent network path may block, so check existence asynchronously! - 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_MS / 2)) != std::future_status::ready) + auto ftDirAvailable = runAsync([=] { return dirAvailable(folderPath); }); + + while (ftDirAvailable.wait_for(std::chrono::milliseconds(rts::UI_UPDATE_INTERVAL_MS / 2)) != std::future_status::ready) onRefreshGui(false /*readyForSync*/); //may throw! - if (!ftDirExists.get()) - return WaitResult(folderPathFmt); - watches.emplace_back(folderPathFmt, std::make_shared<DirWatcher>(folderPathFmt)); //throw FileError + if (!ftDirAvailable.get()) //folder not existing or can't access + return WaitResult(folderPath); + + watches.emplace_back(folderPath, std::make_shared<DirWatcher>(folderPath)); //throw FileError } catch (FileError&) { - if (!somethingExists(folderPathFmt)) //a benign(?) race condition with FileError - return WaitResult(folderPathFmt); + if (!dirAvailable(folderPath)) //folder not existing or can't access + return WaitResult(folderPath); throw; } } @@ -97,7 +98,7 @@ WaitResult waitForChanges(const std::vector<Zstring>& folderPathPhrases, //throw auto lastCheckTime = std::chrono::steady_clock::now(); for (;;) { - const bool checkDirExistNow = [&]() -> bool //checking once per sec should suffice + const bool checkDirNow = [&]() -> bool //checking once per sec should suffice { const auto now = std::chrono::steady_clock::now(); @@ -116,8 +117,8 @@ WaitResult waitForChanges(const std::vector<Zstring>& folderPathPhrases, //throw DirWatcher& watcher = *(it->second); //IMPORTANT CHECK: dirwatcher has problems detecting removal of top watched directories! - if (checkDirExistNow) - if (!dirExists(folderPath)) //catch errors related to directory removal, e.g. ERROR_NETNAME_DELETED -> somethingExists() is NOT sufficient here! + if (checkDirNow) + if (!dirAvailable(folderPath)) //catch errors related to directory removal, e.g. ERROR_NETNAME_DELETED return WaitResult(folderPath); try { @@ -141,7 +142,7 @@ WaitResult waitForChanges(const std::vector<Zstring>& folderPathPhrases, //throw } catch (FileError&) { - if (!somethingExists(folderPath)) //a benign(?) race condition with FileError + if (!dirAvailable(folderPath)) //a benign(?) race condition with FileError return WaitResult(folderPath); throw; } @@ -159,41 +160,42 @@ void waitForMissingDirs(const std::vector<Zstring>& folderPathPhrases, //throw F { for (;;) { - bool allExisting = true; + bool allAvailable = true; //support specifying volume by name => call getResolvedFilePath() repeatedly - for (const Zstring& folderPathFmt : getFormattedDirs(folderPathPhrases)) //throw FileError + for (const Zstring& folderPath : getFormattedDirs(folderPathPhrases)) //throw FileError { - auto ftDirExisting = runAsync([=] + auto ftDirAvailable = runAsync([=] { #ifdef ZEN_WIN //1. login to network share, if necessary -> we probably do NOT want multiple concurrent runs: GUI!? - try{ - connectNetworkShare(folderPathFmt, false /*allowUserInteraction*/); //throw FileError + try + { + connectNetworkShare(folderPath, false /*allowUserInteraction*/); //throw FileError //is this really RTS's job? } catch (FileError&) {} #endif - //2. check dir existence - return zen::dirExists(folderPathFmt); + //2. check dir availability + return dirAvailable(folderPath); }); - while (ftDirExisting.wait_for(std::chrono::milliseconds(rts::UI_UPDATE_INTERVAL_MS / 2)) != std::future_status::ready) - onRefreshGui(folderPathFmt); //may throw! + while (ftDirAvailable.wait_for(std::chrono::milliseconds(rts::UI_UPDATE_INTERVAL_MS / 2)) != std::future_status::ready) + onRefreshGui(folderPath); //may throw! - if (!ftDirExisting.get()) + if (!ftDirAvailable.get()) { - allExisting = false; + allAvailable = false; //wait some time... const int refreshInterval = rts::UI_UPDATE_INTERVAL_MS / 2; static_assert(FOLDER_EXISTENCE_CHECK_INTERVAL_SEC * 1000 % refreshInterval == 0, ""); for (int i = 0; i < FOLDER_EXISTENCE_CHECK_INTERVAL_SEC * 1000 / refreshInterval; ++i) { - onRefreshGui(folderPathFmt); //may throw! + onRefreshGui(folderPath); //may throw! std::this_thread::sleep_for(std::chrono::milliseconds(refreshInterval)); } break; } } - if (allExisting) + if (allAvailable) return; } } @@ -252,7 +254,7 @@ void rts::monitorDirectories(const std::vector<Zstring>& folderPathPhrases, unsi }); switch (res.type) { - case WaitResult::CHANGE_DIR_MISSING: //don't execute the command before all directories are available! + case WaitResult::CHANGE_DIR_UNAVAILABLE: //don't execute the command before all directories are available! callback.setPhase(MonitorCallback::MONITOR_PHASE_WAITING); waitForMissingDirs(folderPathPhrases, [&](const Zstring& folderPath) { callback.requestUiRefresh(); }); //throw FileError callback.setPhase(MonitorCallback::MONITOR_PHASE_ACTIVE); diff --git a/FreeFileSync/Source/algorithm.cpp b/FreeFileSync/Source/algorithm.cpp index 1def34af..1dbc2f5c 100644 --- a/FreeFileSync/Source/algorithm.cpp +++ b/FreeFileSync/Source/algorithm.cpp @@ -1153,26 +1153,36 @@ void copyToAlternateFolderFrom(const std::vector<const FileSystemObject*>& rowsT callback.reportInfo(replaceCpy(statusText, L"%x", fmtPath(displayPath))); }; - const std::wstring txtCreatingFolder(_("Creating folder %x" )); const std::wstring txtCreatingFile (_("Creating file %x" )); + const std::wstring txtCreatingFolder(_("Creating folder %x" )); const std::wstring txtCreatingLink (_("Creating symbolic link %x")); auto copyItem = [&](const FileSystemObject& fsObj, const AbstractPath& targetPath) //throw FileError { - const std::function<void()> deleteTargetItem = [&] + const std::function<void()> removeTargetItem = [&] { if (overwriteIfExists) try { //file or (broken) file-symlink: - AFS::removeFile(targetPath); //throw FileError + AFS::removeFilePlain(targetPath); //throw FileError } catch (FileError&) { - //folder or folder-symlink: - if (AFS::folderExists(targetPath)) //directory or dir-symlink - AFS::removeFolderRecursively(targetPath, nullptr /*onBeforeFileDeletion*/, nullptr /*onBeforeFolderDeletion*/); //throw FileError - else + //(folder-)symlink: + bool symlinkExists = false; + try + { + if (Opt<AFS::ItemType> type = AFS::getItemTypeIfExists(targetPath)) //throw FileError + symlinkExists = *type == AFS::ItemType::SYMLINK; + else + return; + } + catch (FileError&) {} //previous exception is more relevant + + if (symlinkExists) + AFS::removeSymlinkPlain(targetPath); //throw FileError + else //overwrite AFS::ItemType::FOLDER with FILE? => highly dubious, do not allow throw; } }; @@ -1181,13 +1191,20 @@ void copyToAlternateFolderFrom(const std::vector<const FileSystemObject*>& rowsT { StatisticsReporter statReporter(1, 0, callback); notifyItemCopy(txtCreatingFolder, AFS::getDisplayPath(targetPath)); - try { - //deleteTargetItem(); -> never delete pre-existing folders!!! => might delete child items we just copied! + //removeTargetItem(); -> never delete pre-existing folders!!! => might delete child items we just copied before! AFS::copyNewFolder(folder.getAbstractPath<side>(), targetPath, false /*copyFilePermissions*/); //throw FileError } - catch (const FileError&) { if (!AFS::folderExists(targetPath)) throw; } //might already exist: see creation of intermediate directories below + catch (FileError&) + { + bool folderAlreadyExists = false; + try { folderAlreadyExists = AFS::getItemType(targetPath) == AFS::ItemType::FOLDER; } /*throw FileError*/ catch (FileError&) {} //previous exception is more relevant + + if (!folderAlreadyExists) + throw; + //folder might already exist: see creation of intermediate directories below + } statReporter.reportDelta(1, 0); }, @@ -1198,7 +1215,7 @@ void copyToAlternateFolderFrom(const std::vector<const FileSystemObject*>& rowsT auto onNotifyCopyStatus = [&](std::int64_t bytesDelta) { statReporter.reportDelta(0, bytesDelta); }; AFS::copyFileTransactional(file.getAbstractPath<side>(), targetPath, //throw FileError, ErrorFileLocked - false /*copyFilePermissions*/, true /*transactionalCopy*/, deleteTargetItem, onNotifyCopyStatus); + false /*copyFilePermissions*/, true /*transactionalCopy*/, removeTargetItem, onNotifyCopyStatus); statReporter.reportDelta(1, 0); }, @@ -1207,7 +1224,7 @@ void copyToAlternateFolderFrom(const std::vector<const FileSystemObject*>& rowsT StatisticsReporter statReporter(1, 0, callback); notifyItemCopy(txtCreatingLink, AFS::getDisplayPath(targetPath)); - deleteTargetItem(); + removeTargetItem(); AFS::copySymlink(symlink.getAbstractPath<side>(), targetPath, false /*copyFilePermissions*/); //throw FileError statReporter.reportDelta(1, 0); }); @@ -1225,16 +1242,26 @@ void copyToAlternateFolderFrom(const std::vector<const FileSystemObject*>& rowsT } catch (FileError&) { + warn_static("perf: combine with removeTargetItem!!!") //create intermediate directories if missing - const AbstractPath targetParentPath = AFS::appendRelPath(targetFolderPath, beforeLast(relPath, FILE_NAME_SEPARATOR, IF_MISSING_RETURN_NONE)); - if (!AFS::somethingExists(targetParentPath)) //->(minor) file system race condition! + if (Opt<AbstractPath> parentPath = AFS::getParentFolderPath(targetItemPath)) { - AFS::createFolderRecursively(targetParentPath); //throw FileError - //retry: this should work now! - copyItem(*fsObj, targetItemPath); //throw FileError + Opt<AFS::PathDetails> pd; + try { pd = AFS::getPathDetails(*parentPath); /*throw FileError*/ } + catch (FileError&) {} //previous exception is more relevant + + if (pd && !pd->relPath.empty()) //parent folder not existing + { + AbstractPath intermediatePath = pd->existingPath; + for (const Zstring& itemName : pd->relPath) + AFS::createFolderPlain(intermediatePath = AFS::appendRelPath(intermediatePath, itemName)); //throw FileError + + //retry: this should work now! + copyItem(*fsObj, targetItemPath); //throw FileError + return; + } } - else - throw; + throw; } }, callback); //throw X? } @@ -1319,7 +1346,7 @@ void deleteFromGridAndHDOneSide(std::vector<FileSystemObject*>& rowsToDelete, if (useRecycleBin) { notifyItemDeletion(txtRemovingDirectory, AFS::getDisplayPath(folder.getAbstractPath<side>())); - AFS::recycleItemDirectly(folder.getAbstractPath<side>()); //throw FileError + AFS::recycleItemIfExists(folder.getAbstractPath<side>()); //throw FileError statReporter.reportDelta(1, 0); } else @@ -1335,7 +1362,7 @@ void deleteFromGridAndHDOneSide(std::vector<FileSystemObject*>& rowsToDelete, notifyItemDeletion(txtRemovingDirectory, displayPath); }; - AFS::removeFolderRecursively(folder.getAbstractPath<side>(), onBeforeFileDeletion, onBeforeDirDeletion); //throw FileError + AFS::removeFolderIfExistsRecursion(folder.getAbstractPath<side>(), onBeforeFileDeletion, onBeforeDirDeletion); //throw FileError } }, @@ -1344,9 +1371,9 @@ void deleteFromGridAndHDOneSide(std::vector<FileSystemObject*>& rowsToDelete, notifyItemDeletion(txtRemovingFile, AFS::getDisplayPath(file.getAbstractPath<side>())); if (useRecycleBin) - AFS::recycleItemDirectly(file.getAbstractPath<side>()); //throw FileError + AFS::recycleItemIfExists(file.getAbstractPath<side>()); //throw FileError else - AFS::removeFile(file.getAbstractPath<side>()); //throw FileError + AFS::removeFileIfExists(file.getAbstractPath<side>()); //throw FileError statReporter.reportDelta(1, 0); }, @@ -1355,14 +1382,9 @@ void deleteFromGridAndHDOneSide(std::vector<FileSystemObject*>& rowsToDelete, notifyItemDeletion(txtRemovingSymlink, AFS::getDisplayPath(symlink.getAbstractPath<side>())); if (useRecycleBin) - AFS::recycleItemDirectly(symlink.getAbstractPath<side>()); //throw FileError + AFS::recycleItemIfExists(symlink.getAbstractPath<side>()); //throw FileError else - { - if (AFS::folderExists(symlink.getAbstractPath<side>())) //dir symlink - AFS::removeFolderSimple(symlink.getAbstractPath<side>()); //throw FileError - else //file symlink, broken symlink - AFS::removeFile(symlink.getAbstractPath<side>()); //throw FileError - } + AFS::removeSymlinkIfExists(symlink.getAbstractPath<side>()); //throw FileError statReporter.reportDelta(1, 0); }); @@ -1532,7 +1554,7 @@ TempFileBuffer::~TempFileBuffer() if (!tempFolderPath.empty()) try { - removeDirectoryRecursively(tempFolderPath); //throw FileError + removeDirectoryPlainRecursion(tempFolderPath); //throw FileError } catch (FileError&) { assert(false); } } @@ -1572,7 +1594,7 @@ void TempFileBuffer::createTempFiles(const std::set<FileDetails>& workLoad, Proc const uint32_t crc32 = getCrc32(guid); tempPathTmp += printNumber<Zstring>(Zstr("%08x"), static_cast<unsigned int>(crc32)); - makeDirectoryRecursively(tempPathTmp); //throw FileError + createDirectoryIfMissingRecursion(tempPathTmp); //throw FileError tempFolderPath = tempPathTmp; }, callback); //throw X? @@ -1593,7 +1615,7 @@ void TempFileBuffer::createTempFiles(const std::set<FileDetails>& workLoad, Proc const uint16_t crc16 = getCrc16(cookie.ref()); const Zstring detailsHash = printNumber<Zstring>(Zstr("%04x"), static_cast<unsigned int>(crc16)); - const Zstring fileName = AFS::getFileShortName(details.path); + const Zstring fileName = AFS::getItemName(details.path); auto it = std::find(fileName.begin(), fileName.end(), Zchar('.')); //gracefully handle case of missing "." const Zstring tempFileName = Zstring(fileName.begin(), it) + Zchar('-') + detailsHash + Zstring(it, fileName.end()); diff --git a/FreeFileSync/Source/application.cpp b/FreeFileSync/Source/application.cpp index 15aa04a0..66d70579 100644 --- a/FreeFileSync/Source/application.cpp +++ b/FreeFileSync/Source/application.cpp @@ -325,13 +325,13 @@ void Application::launch(const std::vector<Zstring>& commandArgs) { Zstring filePath = getResolvedFilePath(*it); - if (!fileExists(filePath)) //...be a little tolerant + if (!fileAvailable(filePath)) //...be a little tolerant { - if (fileExists(filePath + Zstr(".ffs_batch"))) + if (fileAvailable(filePath + Zstr(".ffs_batch"))) filePath += Zstr(".ffs_batch"); - else if (fileExists(filePath + Zstr(".ffs_gui"))) + else if (fileAvailable(filePath + Zstr(".ffs_gui"))) filePath += Zstr(".ffs_gui"); - else if (fileExists(filePath + Zstr(".xml"))) + else if (fileAvailable(filePath + Zstr(".xml"))) filePath += Zstr(".xml"); else { @@ -549,7 +549,7 @@ void showSyntaxHelp() } -void runBatchMode(const Zstring& globalConfigFile, const XmlBatchConfig& batchCfg, const Zstring& referenceFile, FfsReturnCode& returnCode) +void runBatchMode(const Zstring& globalConfigFilePath, const XmlBatchConfig& batchCfg, const Zstring& referenceFile, FfsReturnCode& returnCode) { auto notifyError = [&](const std::wstring& msg, FfsReturnCode rc) { @@ -562,17 +562,21 @@ void runBatchMode(const Zstring& globalConfigFile, const XmlBatchConfig& batchCf }; XmlGlobalSettings globalCfg; - if (fileExists(globalConfigFile)) //else: globalCfg already has default values try { std::wstring warningMsg; - readConfig(globalConfigFile, globalCfg, warningMsg); //throw FileError + readConfig(globalConfigFilePath, globalCfg, warningMsg); //throw FileError assert(warningMsg.empty()); //ignore parsing errors: should be migration problems only *cross-fingers* } catch (const FileError& e) { - return notifyError(e.toString(), FFS_RC_ABORTED); //abort sync! + bool notExisting = false; + try { notExisting = !getItemTypeIfExists(globalConfigFilePath); /*throw FileError*/ } + catch (FileError&) {} //previous exception is more relevant + + if (!notExisting) //existing or access error + return notifyError(e.toString(), FFS_RC_ABORTED); //abort sync! } try @@ -661,12 +665,12 @@ void runBatchMode(const Zstring& globalConfigFile, const XmlBatchConfig& batchCf catch (BatchRequestSwitchToMainDialog&) { //open new toplevel window *after* progress dialog is gone => run on main event loop - return MainDialog::create(globalConfigFile, &globalCfg, xmlAccess::convertBatchToGui(batchCfg), { referenceFile }, true /*startComparison*/); + return MainDialog::create(globalConfigFilePath, &globalCfg, xmlAccess::convertBatchToGui(batchCfg), { referenceFile }, true /*startComparison*/); } try //save global settings to XML: e.g. ignored warnings { - xmlAccess::writeConfig(globalCfg, globalConfigFile); //FileError + xmlAccess::writeConfig(globalCfg, globalConfigFilePath); //FileError } catch (const FileError& e) { diff --git a/FreeFileSync/Source/comparison.cpp b/FreeFileSync/Source/comparison.cpp index 36318542..dadfd83e 100644 --- a/FreeFileSync/Source/comparison.cpp +++ b/FreeFileSync/Source/comparison.cpp @@ -84,11 +84,11 @@ ResolvedBaseFolders initializeBaseFolders(const std::vector<FolderPairCfg>& cfgL const FolderStatus status = getFolderStatusNonBlocking(uniqueBaseFolders, folderAccessTimeout, allowUserInteraction, callback); //re-check *all* directories on each try! output.existingBaseFolders = status.existing; - if (!status.missing.empty() || !status.failedChecks.empty()) + if (!status.notExisting.empty() || !status.failedChecks.empty()) { std::wstring errorMsg = _("Cannot find the following folders:") + L"\n"; - for (const AbstractPath& folderPath : status.missing) + for (const AbstractPath& folderPath : status.notExisting) errorMsg += L"\n" + AFS::getDisplayPath(folderPath); for (const auto& fc : status.failedChecks) @@ -202,14 +202,14 @@ ComparisonBuffer::ComparisonBuffer(const std::set<DirectoryKey>& keysToRead, int switch (callback_.reportError(msg, retryNumber)) { case ProcessCallback::IGNORE_ERROR: - return ON_ERROR_IGNORE; + return ON_ERROR_CONTINUE; case ProcessCallback::RETRY: return ON_ERROR_RETRY; } assert(false); - return ON_ERROR_IGNORE; + return ON_ERROR_CONTINUE; } private: @@ -367,29 +367,22 @@ std::shared_ptr<BaseFolderPair> ComparisonBuffer::compareByTimeSize(const Resolv void categorizeSymlinkByContent(SymlinkPair& symlink, ProcessCallback& callback) { //categorize symlinks that exist on both sides - Zstring targetPathRawL; - Zstring targetPathRawR; + std::string binaryContentL; + std::string binaryContentR; Opt<std::wstring> errMsg = tryReportingError([&] { callback.reportStatus(replaceCpy(_("Resolving symbolic link %x"), L"%x", fmtPath(AFS::getDisplayPath(symlink.getAbstractPath<LEFT_SIDE>())))); - - targetPathRawL = AFS::getSymlinkContentBuffer(symlink.getAbstractPath<LEFT_SIDE>()); //throw FileError + binaryContentL = AFS::getSymlinkBinaryContent(symlink.getAbstractPath<LEFT_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 + binaryContentR = AFS::getSymlinkBinaryContent(symlink.getAbstractPath<RIGHT_SIDE>()); //throw FileError }, callback); //throw X? if (errMsg) symlink.setCategoryConflict(*errMsg); else { - if (targetPathRawL == targetPathRawR -#ifdef ZEN_WIN //type of symbolic link is relevant for Windows only - && - AFS::folderExists(symlink.getAbstractPath< LEFT_SIDE>()) == //check if dir-symlink - AFS::folderExists(symlink.getAbstractPath<RIGHT_SIDE>()) // -#endif - ) + if (binaryContentL == binaryContentR) { //Caveat: //1. SYMLINK_EQUAL may only be set if short names match in case: InSyncFolder's mapping tables use short name as a key! see db_file.cpp @@ -612,9 +605,9 @@ void MergeSides::fillOneSide(const FolderContainer& folderCont, const std::wstri for (const auto& dir : folderCont.folders) { - FolderPair& newFolder = output.addSubFolder<side>(dir.first); + FolderPair& newFolder = output.addSubFolder<side>(dir.first, dir.second.first); const std::wstring* errorMsgNew = checkFailedRead(newFolder, errorMsg); - fillOneSide<side>(dir.second, errorMsgNew, newFolder); //recurse + fillOneSide<side>(dir.second.second, errorMsgNew, newFolder); //recurse } } @@ -704,27 +697,27 @@ void MergeSides::mergeTwoSides(const FolderContainer& lhs, const FolderContainer linearMerge(lhs.folders, rhs.folders, [&](const FolderData& dirLeft) //left only { - FolderPair& newFolder = output.addSubFolder<LEFT_SIDE>(dirLeft.first); + FolderPair& newFolder = output.addSubFolder<LEFT_SIDE>(dirLeft.first, dirLeft.second.first); const std::wstring* errorMsgNew = checkFailedRead(newFolder, errorMsg); - this->fillOneSide<LEFT_SIDE>(dirLeft.second, errorMsgNew, newFolder); //recurse + this->fillOneSide<LEFT_SIDE>(dirLeft.second.second, errorMsgNew, newFolder); //recurse }, [&](const FolderData& dirRight) //right only { - FolderPair& newFolder = output.addSubFolder<RIGHT_SIDE>(dirRight.first); + FolderPair& newFolder = output.addSubFolder<RIGHT_SIDE>(dirRight.first, dirRight.second.first); const std::wstring* errorMsgNew = checkFailedRead(newFolder, errorMsg); - this->fillOneSide<RIGHT_SIDE>(dirRight.second, errorMsgNew, newFolder); //recurse + this->fillOneSide<RIGHT_SIDE>(dirRight.second.second, errorMsgNew, newFolder); //recurse }, [&](const FolderData& dirLeft, const FolderData& dirRight) //both sides { - FolderPair& newFolder = output.addSubFolder(dirLeft.first, dirRight.first, DIR_EQUAL); + FolderPair& newFolder = output.addSubFolder(dirLeft.first, dirLeft.second.first, DIR_EQUAL, dirRight.first, dirRight.second.first); const std::wstring* errorMsgNew = checkFailedRead(newFolder, errorMsg); if (!errorMsgNew) if (dirLeft.first != dirRight.first) newFolder.setCategoryDiffMetadata(getDescrDiffMetaShortnameCase(newFolder)); - mergeTwoSides(dirLeft.second, dirRight.second, errorMsgNew, newFolder); //recurse + mergeTwoSides(dirLeft.second.second, dirRight.second.second, errorMsgNew, newFolder); //recurse }); } diff --git a/FreeFileSync/Source/file_hierarchy.h b/FreeFileSync/Source/file_hierarchy.h index 401c921b..6a41af2d 100644 --- a/FreeFileSync/Source/file_hierarchy.h +++ b/FreeFileSync/Source/file_hierarchy.h @@ -57,6 +57,16 @@ struct LinkDescriptor }; +struct FolderDescriptor +{ + FolderDescriptor() {} + FolderDescriptor(bool isSymlink) : + isFollowedSymlink(isSymlink) {} + + bool isFollowedSymlink = false; +}; + + enum SelectedSide { LEFT_SIDE, @@ -95,7 +105,7 @@ struct SelectParam<RIGHT_SIDE> struct FolderContainer { //------------------------------------------------------------------ - using FolderList = std::map<Zstring, FolderContainer, LessFilePath>; // + using FolderList = std::map<Zstring, std::pair<FolderDescriptor, FolderContainer>, LessFilePath>; // using FileList = std::map<Zstring, FileDescriptor, LessFilePath>; //key: file name using SymlinkList = std::map<Zstring, LinkDescriptor, LessFilePath>; // //------------------------------------------------------------------ @@ -104,29 +114,34 @@ struct FolderContainer FolderContainer (const FolderContainer&) = delete; //catch accidental (and unnecessary) copying FolderContainer& operator=(const FolderContainer&) = delete; // - FolderList folders; FileList files; SymlinkList symlinks; //non-followed symlinks + FolderList folders; - //convenience - FolderContainer& addSubFolder(const Zstring& itemName) + void addSubFile(const Zstring& itemName, const FileDescriptor& descr) { - return folders[itemName]; //value default-construction is okay here - //return dirs.emplace(itemName, FolderContainer()).first->second; + auto rv = files.emplace(itemName, descr); + if (!rv.second) //update entry if already existing (e.g. during folder traverser "retry") => does not handle different item name case (irrelvant!..) + rv.first->second = descr; } - void addSubFile(const Zstring& itemName, const FileDescriptor& fileData) + void addSubLink(const Zstring& itemName, const LinkDescriptor& descr) { - auto rv = files.emplace(itemName, fileData); - if (!rv.second) //update entry if already existing (e.g. during folder traverser "retry") => does not handle different item name case (irrelvant!..) - rv.first->second = fileData; + auto rv = symlinks.emplace(itemName, descr); + if (!rv.second) + rv.first->second = descr; } - void addSubLink(const Zstring& itemName, const LinkDescriptor& linkData) + FolderContainer& addSubFolder(const Zstring& itemName, const FolderDescriptor& descr) { - auto rv = symlinks.emplace(itemName, linkData); - if (!rv.second) - rv.first->second = linkData; + auto& p = folders[itemName]; //value default-construction is okay here + p.first = descr; + return p.second; + + //auto rv = folders.emplace(itemName, std::pair<FolderDescriptor, FolderContainer>(descr, FolderContainer())); + //if (!rv.second) + // rv.first->second.first = descr; + //return rv.first->second.second; } }; @@ -161,12 +176,14 @@ public: using FolderList = FixedList<FolderPair>; FolderPair& addSubFolder(const Zstring& itemNameLeft, + const FolderDescriptor& left, //file exists on both sides + CompareDirResult defaultCmpResult, const Zstring& itemNameRight, - CompareDirResult defaultCmpResult); + const FolderDescriptor& right); template <SelectedSide side> - FolderPair& addSubFolder(const Zstring& itemName); //dir exists on one side only - + FolderPair& addSubFolder(const Zstring& itemName, //dir exists on one side only + const FolderDescriptor& descr); FilePair& addSubFile(const Zstring& itemNameLeft, const FileDescriptor& left, //file exists on both sides @@ -233,17 +250,17 @@ class BaseFolderPair : public HierarchyObject //synchronization base directory { public: BaseFolderPair(const AbstractPath& folderPathLeft, - bool dirExistsLeft, + bool folderAvailableLeft, const AbstractPath& folderPathRight, - bool dirExistsRight, + bool folderAvailableRight, const HardFilter::FilterRef& filter, CompareVariant cmpVar, int fileTimeTolerance, const std::vector<unsigned int>& ignoreTimeShiftMinutes) : HierarchyObject(Zstring(), *this), filter_(filter), cmpVar_(cmpVar), fileTimeTolerance_(fileTimeTolerance), ignoreTimeShiftMinutes_(ignoreTimeShiftMinutes), - dirExistsLeft_ (dirExistsLeft), - dirExistsRight_(dirExistsRight), + folderAvailableLeft_ (folderAvailableLeft), + folderAvailableRight_(folderAvailableRight), folderPathLeft_(folderPathLeft), folderPathRight_(folderPathRight) {} @@ -251,8 +268,8 @@ public: 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 + template <SelectedSide side> bool isAvailable() const; //base folder status at the time of comparison! + template <SelectedSide side> void setAvailable(bool value); //update after creating the directory in FFS //get settings which were used while creating BaseFolderPair const HardFilter& getFilter() const { return *filter_; } @@ -268,8 +285,8 @@ private: const int fileTimeTolerance_; const std::vector<unsigned int> ignoreTimeShiftMinutes_; - bool dirExistsLeft_; - bool dirExistsRight_; + bool folderAvailableLeft_; + bool folderAvailableRight_; AbstractPath folderPathLeft_; AbstractPath folderPathRight_; @@ -469,15 +486,22 @@ public: CompareDirResult getDirCategory() const; //returns actually used subset of CompareFilesResult FolderPair(const Zstring& itemNameLeft, //use empty itemName if "not existing" - const Zstring& itemNameRight, // - HierarchyObject& parentObj, - CompareDirResult defaultCmpResult) : + const FolderDescriptor& left, + CompareDirResult defaultCmpResult, + const Zstring& itemNameRight, + const FolderDescriptor& right, + HierarchyObject& parentObj) : FileSystemObject(itemNameLeft, itemNameRight, parentObj, static_cast<CompareFilesResult>(defaultCmpResult)), - HierarchyObject(getPairRelativePath() + FILE_NAME_SEPARATOR, parentObj.getBase()) {} + HierarchyObject(getPairRelativePath() + FILE_NAME_SEPARATOR, parentObj.getBase()), + dataLeft_(left), + dataRight_(right) {} + + template <SelectedSide side> bool isFollowedSymlink() const; SyncOperation getSyncOperation() const override; - void setSyncedTo(const Zstring& itemName); //call after sync, sets DIR_EQUAL + template <SelectedSide sideTrg> + void setSyncedTo(const Zstring& itemName, bool isSymlinkTrg, bool isSymlinkSrc); //call after sync, sets DIR_EQUAL private: void flip () override; @@ -487,6 +511,9 @@ private: mutable SyncOperation syncOpBuffered_ = SO_DO_NOTHING; //determining sync-op for directory may be expensive as it depends on child-objects -> buffer it mutable bool haveBufferedSyncOp_ = false; // + + FolderDescriptor dataLeft_; + FolderDescriptor dataRight_; }; //------------------------------------------------------------------ @@ -859,26 +886,28 @@ void HierarchyObject::flip() inline FolderPair& HierarchyObject::addSubFolder(const Zstring& itemNameLeft, + const FolderDescriptor& left, + CompareDirResult defaultCmpResult, const Zstring& itemNameRight, - CompareDirResult defaultCmpResult) + const FolderDescriptor& right) { - subFolders_.emplace_back(itemNameLeft, itemNameRight, *this, defaultCmpResult); + subFolders_.emplace_back(itemNameLeft, left, defaultCmpResult, itemNameRight, right, *this); return subFolders_.back(); } template <> inline -FolderPair& HierarchyObject::addSubFolder<LEFT_SIDE>(const Zstring& itemName) +FolderPair& HierarchyObject::addSubFolder<LEFT_SIDE>(const Zstring& itemName, const FolderDescriptor& descr) { - subFolders_.emplace_back(itemName, Zstring(), *this, DIR_LEFT_SIDE_ONLY); + subFolders_.emplace_back(itemName, descr, DIR_LEFT_SIDE_ONLY, Zstring(), FolderDescriptor(), *this); return subFolders_.back(); } template <> inline -FolderPair& HierarchyObject::addSubFolder<RIGHT_SIDE>(const Zstring& itemName) +FolderPair& HierarchyObject::addSubFolder<RIGHT_SIDE>(const Zstring& itemName, const FolderDescriptor& descr) { - subFolders_.emplace_back(Zstring(), itemName, *this, DIR_RIGHT_SIDE_ONLY); + subFolders_.emplace_back(Zstring(), FolderDescriptor(), DIR_RIGHT_SIDE_ONLY, itemName, descr, *this); return subFolders_.back(); } @@ -943,8 +972,8 @@ inline void BaseFolderPair::flip() { HierarchyObject::flip(); - std::swap(dirExistsLeft_, dirExistsRight_); - std::swap(folderPathLeft_, folderPathRight_); + std::swap(folderAvailableLeft_, folderAvailableRight_); + std::swap(folderPathLeft_, folderPathRight_); } @@ -953,6 +982,7 @@ void FolderPair::flip() { HierarchyObject ::flip(); //call base class versions FileSystemObject::flip(); // + std::swap(dataLeft_, dataRight_); } @@ -965,6 +995,8 @@ void FolderPair::removeObjectL() link.removeObject<LEFT_SIDE>(); for (FolderPair& folder : refSubFolders()) folder.removeObject<LEFT_SIDE>(); + + dataLeft_ = FolderDescriptor(); } @@ -977,20 +1009,22 @@ void FolderPair::removeObjectR() link.removeObject<RIGHT_SIDE>(); for (FolderPair& folder : refSubFolders()) folder.removeObject<RIGHT_SIDE>(); + + dataRight_ = FolderDescriptor(); } template <SelectedSide side> inline -bool BaseFolderPair::isExisting() const +bool BaseFolderPair::isAvailable() const { - return SelectParam<side>::ref(dirExistsLeft_, dirExistsRight_); + return SelectParam<side>::ref(folderAvailableLeft_, folderAvailableRight_); } template <SelectedSide side> inline -void BaseFolderPair::setExisting(bool value) +void BaseFolderPair::setAvailable(bool value) { - SelectParam<side>::ref(dirExistsLeft_, dirExistsRight_) = value; + SelectParam<side>::ref(folderAvailableLeft_, folderAvailableRight_) = value; } @@ -1030,6 +1064,13 @@ bool FilePair::isFollowedSymlink() const } +template <SelectedSide side> inline +bool FolderPair::isFollowedSymlink() const +{ + return SelectParam<side>::ref(dataLeft_, dataRight_).isFollowedSymlink; +} + + template <SelectedSide sideTrg> inline void FilePair::setSyncedTo(const Zstring& itemName, std::uint64_t fileSize, @@ -1065,9 +1106,16 @@ void SymlinkPair::setSyncedTo(const Zstring& itemName, } -inline -void FolderPair::setSyncedTo(const Zstring& itemName) +template <SelectedSide sideTrg> inline +void FolderPair::setSyncedTo(const Zstring& itemName, + bool isSymlinkTrg, + bool isSymlinkSrc) { + static const SelectedSide sideSrc = OtherSide<sideTrg>::result; + + SelectParam<sideTrg>::ref(dataLeft_, dataRight_) = FolderDescriptor(isSymlinkTrg); + SelectParam<sideSrc>::ref(dataLeft_, dataRight_) = FolderDescriptor(isSymlinkSrc); + FileSystemObject::setSynced(itemName); //set FileSystemObject specific part } diff --git a/FreeFileSync/Source/fs/abstract.cpp b/FreeFileSync/Source/fs/abstract.cpp index 26ac4445..fa2caf91 100644 --- a/FreeFileSync/Source/fs/abstract.cpp +++ b/FreeFileSync/Source/fs/abstract.cpp @@ -58,7 +58,7 @@ AFS::FileAttribAfterCopy AFS::copyFileTransactional(const AbstractPath& apSource throw FileError(replaceCpy(_("Cannot write permissions of %x."), L"%x", fmtPath(AFS::getDisplayPath(apTargetTmp))), _("Operation not supported for different base folder types.")); - return AFS::copyFileAsStream(apSource, apTargetTmp, notifyProgress); //throw FileError, ErrorTargetExisting, ErrorFileLocked + return apSource.afs->copyFileAsStream(apSource.itemPathImpl, apTargetTmp, notifyProgress); //throw FileError, ErrorTargetExisting, ErrorFileLocked }; if (transactionalCopy) @@ -79,7 +79,7 @@ AFS::FileAttribAfterCopy AFS::copyFileTransactional(const AbstractPath& apSource } //transactional behavior: ensure cleanup; not needed before copyFileBestEffort() which is already transactional - ZEN_ON_SCOPE_FAIL( try { AFS::removeFile(apTargetTmp); } + ZEN_ON_SCOPE_FAIL( try { AFS::removeFilePlain(apTargetTmp); } catch (FileError&) {} ); //have target file deleted (after read access on source and target has been confirmed) => allow for almost transactional overwrite @@ -115,22 +115,26 @@ AFS::FileAttribAfterCopy AFS::copyFileTransactional(const AbstractPath& apSource } -void AFS::createFolderRecursively(const AbstractPath& ap) //throw FileError +void AFS::createFolderIfMissingRecursion(const AbstractPath& ap) //throw FileError { + if (!getParentFolderPath(ap)) //device root + return static_cast<void>(/*ItemType =*/ getItemType(ap)); //throw FileError + try { - AFS::createFolderSimple(ap); //throw FileError, ErrorTargetExisting, ErrorTargetPathMissing + createFolderPlain(ap); //throw FileError } - catch (ErrorTargetExisting&) {} - catch (ErrorTargetPathMissing&) + catch (FileError&) { - if (Opt<AbstractPath> parentPath = AFS::getParentFolderPath(ap)) - { - //recurse... - createFolderRecursively(*parentPath); //throw FileError + Opt<PathDetails> pd; + try { pd = getPathDetails(ap); /*throw FileError*/ } + catch (FileError&) {} //previous exception is more relevant - //now try again... - AFS::createFolderSimple(ap); //throw FileError, (ErrorTargetExisting), (ErrorTargetPathMissing) + if (pd && pd->existingType != ItemType::FILE) + { + AbstractPath intermediatePath = pd->existingPath; + for (const Zstring& itemName : pd->relPath) + createFolderPlain(intermediatePath = appendRelPath(intermediatePath, itemName)); //throw FileError return; } throw; @@ -140,41 +144,149 @@ void AFS::createFolderRecursively(const AbstractPath& ap) //throw FileError namespace { -struct FlatTraverserCallback: public AFS::TraverserCallback +struct ItemSearchCallback: public AFS::TraverserCallback { - FlatTraverserCallback(const AbstractPath& folderPath) : folderPath_(folderPath) {} + ItemSearchCallback(const Zstring& itemName) : itemName_(itemName) {} + + void onFile (const FileInfo& fi) override { if (equalFilePath(fi.itemName, itemName_)) throw AFS::ItemType::FILE; } + std::unique_ptr<TraverserCallback> onFolder (const FolderInfo& fi) override { if (equalFilePath(fi.itemName, itemName_)) throw AFS::ItemType::FOLDER; return nullptr; } + HandleLink onSymlink(const SymlinkInfo& si) override { if (equalFilePath(si.itemName, itemName_)) throw AFS::ItemType::SYMLINK; return TraverserCallback::LINK_SKIP; } + 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); } + +private: + const Zstring itemName_; +}; +} + + +AFS::PathDetailsImpl AFS::getPathDetailsViaFolderTraversal(const Zstring& itemPathImpl) const //throw FileError +{ + const Opt<Zstring> parentPathImpl = getParentFolderPathImpl(itemPathImpl); + try + { + return { getItemType(itemPathImpl), itemPathImpl, {} }; //throw FileError + } + catch (FileError&) + { + if (!parentPathImpl) //device root + throw; + //else: let's dig deeper... don't bother checking Win32 codes; e.g. not existing item may have the codes: + // ERROR_FILE_NOT_FOUND, ERROR_PATH_NOT_FOUND, ERROR_INVALID_NAME, ERROR_INVALID_DRIVE, + // ERROR_NOT_READY, ERROR_INVALID_PARAMETER, ERROR_BAD_PATHNAME, ERROR_BAD_NETPATH => not reliable + } + const Zstring itemName = getItemName(itemPathImpl); + assert(!itemName.empty()); - 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 + PathDetailsImpl pd = getPathDetailsViaFolderTraversal(*parentPathImpl); //throw FileError + if (!pd.relPath.empty()) { - 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); - return TraverserCallback::LINK_SKIP; + pd.relPath.push_back(itemName); + return { pd.existingType, pd.existingPathImpl, pd.relPath }; } + + try + { + ItemSearchCallback iscb(itemName); + traverseFolder(*parentPathImpl, iscb); //throw FileError, ItemType + + return { pd.existingType, *parentPathImpl, { itemName } }; //throw FileError + } + catch (const ItemType& type) { return { type, itemPathImpl, {} }; } //yes, exceptions for control-flow are bad design... but, but... + //we're not CPU-bound here and finding the item after getItemType() previously failed is exceptional (even C:\pagefile.sys should be found) +} + + +Opt<AFS::ItemType> AFS::getItemTypeIfExists(const AbstractPath& ap) //throw FileError +{ + const PathDetails pd = getPathDetails(ap); //throw FileError +#ifndef NDEBUG + AbstractPath reconstructedPath = pd.existingPath; + for (const Zstring& itemName : pd.relPath) + reconstructedPath = appendRelPath(reconstructedPath, itemName); + assert(equalAbstractPath(ap, reconstructedPath)); +#endif + if (pd.relPath.empty()) + return pd.existingType; + return NoValue(); +} + + +AFS::PathDetails AFS::getPathDetails(const AbstractPath& ap) //throw FileError +{ + const PathDetailsImpl pdi = ap.afs->getPathDetails(ap.itemPathImpl); //throw FileError + return { pdi.existingType, AbstractPath(ap.afs, pdi.existingPathImpl), pdi.relPath }; +} + + +//boost::variant<AFS::ItemType, AFS::NotExistType> AFS::getItemTypeIfExistsByFolderTraversal(const Zstring& itemPathImpl) const //throw FileError +//{ +// const Opt<Zstring> parentPathImpl = getParentFolderPathImpl(itemPathImpl); +// try +// { +// return getItemType(itemPathImpl); //throw FileError +// //bonus: still find intermediate hidden/access-denied SFTP folders +// } +// catch (FileError&) +// { +// if (!parentPathImpl) +// throw; +// //else: let's dig deeper... don't bother checking Win32 codes; e.g. not existing item may have the codes: +// // ERROR_FILE_NOT_FOUND, ERROR_PATH_NOT_FOUND, ERROR_INVALID_NAME, ERROR_INVALID_DRIVE, +// // ERROR_NOT_READY, ERROR_INVALID_PARAMETER, ERROR_BAD_PATHNAME, ERROR_BAD_NETPATH => not reliable +// } +// const Zstring itemName = getItemName(itemPathImpl); +// assert(!itemName.empty()); +// try +// { +// ItemSearchCallback iscb(itemName); +// traverseFolder(*parentPathImpl, iscb); //throw FileError, ItemType +// return NotExistType::PARENT_EXISTING; +// } +// catch (const ItemType& type) { return type; } //yes, exceptions for control-flow are bad design... but, but... +// //we're not CPU-bound here and finding the item after getItemType() previously failed is exceptional (even C:\pagefile.sys should be found) +// catch (FileError&) +// { +// const auto var = getItemTypeIfExistsByFolderTraversal(*parentPathImpl); //throw FileError +// if (boost::get<NotExistType>(&var)) +// return NotExistType::PARENT_NOT_EXISTING; +// throw; +// } +//} + + +namespace +{ +struct FlatTraverserCallback: public AFS::TraverserCallback +{ + FlatTraverserCallback(const AbstractPath& folderPath) : folderPath_(folderPath) {} + + void onFile (const FileInfo& fi) override { fileNames_ .push_back(fi.itemName); } + std::unique_ptr<TraverserCallback> onFolder (const FolderInfo& fi) override { folderNames_ .push_back(fi.itemName); return nullptr; } + HandleLink onSymlink(const SymlinkInfo& si) override { symlinkNames_.push_back(si.itemName); return TraverserCallback::LINK_SKIP; } 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 std::vector<Zstring>& refFileNames () const { return fileNames_; } - const std::vector<Zstring>& refFolderNames () const { return folderNames_; } - const std::vector<Zstring>& refFolderLinkNames() const { return folderLinkNames_; } + const std::vector<Zstring>& refFileNames () const { return fileNames_; } + const std::vector<Zstring>& refFolderNames () const { return folderNames_; } + const std::vector<Zstring>& refSymlinkNames() const { return symlinkNames_; } private: const AbstractPath folderPath_; std::vector<Zstring> fileNames_; std::vector<Zstring> folderNames_; - std::vector<Zstring> folderLinkNames_; + std::vector<Zstring> symlinkNames_; }; -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! +void removeFolderIfExistsRecursionImpl(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(!AFS::symlinkExists(folderPath)); //[!] no symlinks in this context!!! - assert(AFS::folderExists(folderPath)); //Do NOT traverse into it deleting contained files!!! +#ifndef NDEBUG //[!] no symlinks in this context!!! Do NOT traverse into it deleting contained files!!! + try { assert(AFS::getItemType(folderPath) != AFS::ItemType::SYMLINK); /*throw FileError*/ } + catch (FileError&) {} +#endif FlatTraverserCallback ft(folderPath); //deferred recursion => save stack space and allow deletion of extremely deep hierarchies! AFS::traverseFolder(folderPath, ft); //throw FileError @@ -185,45 +297,91 @@ void removeFolderRecursivelyImpl(const AbstractPath& folderPath, //throw FileErr if (onBeforeFileDeletion) onBeforeFileDeletion(AFS::getDisplayPath(filePath)); - AFS::removeFile(filePath); //throw FileError + AFS::removeFilePlain(filePath); //throw FileError } - for (const Zstring& folderLinkName : ft.refFolderLinkNames()) + for (const Zstring& symlinkName : ft.refSymlinkNames()) { - const AbstractPath linkPath = AFS::appendRelPath(folderPath, folderLinkName); - if (onBeforeFolderDeletion) - onBeforeFolderDeletion(AFS::getDisplayPath(linkPath)); + const AbstractPath linkPath = AFS::appendRelPath(folderPath, symlinkName); + if (onBeforeFileDeletion) + onBeforeFileDeletion(AFS::getDisplayPath(linkPath)); - AFS::removeFolderSimple(linkPath); //throw FileError + AFS::removeSymlinkPlain(linkPath); //throw FileError } for (const Zstring& folderName : ft.refFolderNames()) - removeFolderRecursivelyImpl(AFS::appendRelPath(folderPath, folderName), //throw FileError - onBeforeFileDeletion, onBeforeFolderDeletion); + removeFolderIfExistsRecursionImpl(AFS::appendRelPath(folderPath, folderName), //throw FileError + onBeforeFileDeletion, onBeforeFolderDeletion); if (onBeforeFolderDeletion) onBeforeFolderDeletion(AFS::getDisplayPath(folderPath)); - AFS::removeFolderSimple(folderPath); //throw FileError + AFS::removeFolderPlain(folderPath); //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! +void AFS::removeFolderIfExistsRecursion(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 (AFS::symlinkExists(ap)) + if (Opt<ItemType> type = AFS::getItemTypeIfExists(ap)) //throw FileError { - if (onBeforeFolderDeletion) - onBeforeFolderDeletion(AFS::getDisplayPath(ap)); + if (*type == AFS::ItemType::SYMLINK) + { + if (onBeforeFileDeletion) + onBeforeFileDeletion(AFS::getDisplayPath(ap)); - AFS::removeFolderSimple(ap); //throw FileError + AFS::removeSymlinkPlain(ap); //throw FileError + } + else + removeFolderIfExistsRecursionImpl(ap, onBeforeFileDeletion, onBeforeFolderDeletion); //throw FileError } - else + //no error situation if directory is not existing! manual deletion relies on it! +} + + +bool AFS::removeFileIfExists(const AbstractPath& ap) //throw FileError +{ + try + { + removeFilePlain(ap); //throw FileError + return true; + } + catch (FileError&) + { + bool itemNotExisting = false; + try + { + itemNotExisting = !getItemTypeIfExists(ap); //throw FileError + } + catch (FileError&) {} //previous exception is more relevant + + if (itemNotExisting) + return false; + throw; + } +} + + +bool AFS::removeSymlinkIfExists(const AbstractPath& ap) //throw FileError +{ + try + { + AFS::removeSymlinkPlain(ap); //throw FileError + return true; + } + catch (FileError&) { - //no error situation if directory is not existing! manual deletion relies on it! - if (AFS::somethingExists(ap)) - removeFolderRecursivelyImpl(ap, onBeforeFileDeletion, onBeforeFolderDeletion); //throw FileError + bool itemNotExisting = false; + try + { + itemNotExisting = !getItemTypeIfExists(ap); //throw FileError + } + catch (FileError&) {} //previous exception is more relevant + + if (itemNotExisting) + return false; + throw; } } diff --git a/FreeFileSync/Source/fs/abstract.h b/FreeFileSync/Source/fs/abstract.h index 86688543..b6b3142a 100644 --- a/FreeFileSync/Source/fs/abstract.h +++ b/FreeFileSync/Source/fs/abstract.h @@ -53,7 +53,7 @@ struct AbstractFileSystem //THREAD-SAFETY: "const" member functions must model t }; static Opt<PathComplement> getPathComplement(const AbstractPath& lhs, const AbstractPath& rhs); - static Zstring getFileShortName(const AbstractPath& ap) { return ap.afs->getFileShortName(ap.itemPathImpl); } + static Zstring getItemName(const AbstractPath& ap) { assert(getParentFolderPath(ap)); return ap.afs->getItemName(ap.itemPathImpl); } static bool havePathDependency(const AbstractPath& lhs, const AbstractPath& rhs) { @@ -71,43 +71,53 @@ struct AbstractFileSystem //THREAD-SAFETY: "const" member functions must model t } //---------------------------------------------------------------------------------------------------------------- - 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 + enum class ItemType + { + FILE, + FOLDER, + SYMLINK, + }; + struct PathDetails + { + ItemType existingType; + AbstractPath existingPath; //itemPath =: existingPath + relPath + std::vector<Zstring> relPath; // + }; + //(hopefully) fast: does not distinguish between error/not existing + static ItemType getItemType(const AbstractPath& ap) { return ap.afs->getItemType(ap.itemPathImpl); } //throw FileError + //execute potentially SLOW folder traversal but distinguish error/not existing + static Opt<ItemType> getItemTypeIfExists(const AbstractPath& ap); //throw FileError + static PathDetails getPathDetails(const AbstractPath& ap); //throw FileError //---------------------------------------------------------------------------------------------------------------- - //should provide for single ATOMIC folder creation! - static void createFolderSimple(const AbstractPath& ap) { ap.afs->createFolderSimple(ap.itemPathImpl); } //throw FileError, ErrorTargetExisting, ErrorTargetPathMissing - - //non-recursive folder deletion: - static void removeFolderSimple(const AbstractPath& ap) { ap.afs->removeFolderSimple(ap.itemPathImpl); } //throw FileError + //- error if already existing + //- does NOT create parent directories recursively if not existing + static void createFolderPlain(const AbstractPath& ap) { ap.afs->createFolderPlain(ap.itemPathImpl); } //throw FileError //- no error if already existing - //- create recursively if parent directory is not existing - static void createFolderRecursively(const AbstractPath& 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 AbstractPath& ap) { return ap.afs->removeFile(ap.itemPathImpl); } //throw FileError; return "false" if file is not existing - + //- creates parent directories recursively if not existing + static void createFolderIfMissingRecursion(const AbstractPath& ap); //throw FileError + + static bool removeFileIfExists (const AbstractPath& ap); //throw FileError; return "false" if file is not existing + static bool removeSymlinkIfExists(const AbstractPath& ap); // + static void removeFolderIfExistsRecursion(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 void removeFilePlain (const AbstractPath& ap) { ap.afs->removeFilePlain (ap.itemPathImpl); } //throw FileError + static void removeSymlinkPlain(const AbstractPath& ap) { ap.afs->removeSymlinkPlain(ap.itemPathImpl); } //throw FileError + static void removeFolderPlain (const AbstractPath& ap) { ap.afs->removeFolderPlain (ap.itemPathImpl); } //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 AbstractPath getResolvedSymlinkPath(const AbstractPath& ap) { return AbstractPath(ap.afs, ap.afs->getResolvedSymlinkPath(ap.itemPathImpl)); } //throw FileError - - static Zstring getSymlinkContentBuffer(const AbstractPath& ap) { return ap.afs->getSymlinkContentBuffer(ap.itemPathImpl); } //throw FileError - + static std::string getSymlinkBinaryContent(const AbstractPath& ap) { return ap.afs->getSymlinkBinaryContent(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); } - 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 //---------------------------------------------------------------------------------------------------------------- @@ -128,7 +138,7 @@ struct AbstractFileSystem //THREAD-SAFETY: "const" member functions must model t { virtual ~OutputStreamImpl() {} virtual size_t getBlockSize() const = 0; //non-zero block size is AFS contract! - virtual size_t tryWrite(const void* data, size_t len) = 0; //throw FileError; may return short! CONTRACT: bytesToWrite > 0 + virtual size_t tryWrite(const void* buffer, size_t bytesToWrite) = 0; //throw FileError; may return short! CONTRACT: bytesToWrite > 0 virtual FileId finalize(const std::function<void()>& onUpdateStatus) = 0; //throw FileError }; @@ -163,39 +173,40 @@ struct AbstractFileSystem //THREAD-SAFETY: "const" member functions must model t struct SymlinkInfo { - const Zstring itemName; + Zstring itemName; std::int64_t lastWriteTime; //number of seconds since Jan. 1st 1970 UTC }; struct FileInfo { - const Zstring itemName; + Zstring itemName; std::uint64_t fileSize; //unit: bytes! std::int64_t lastWriteTime; //number of seconds since Jan. 1st 1970 UTC - const FileId id; //optional: empty if not supported! + FileId id; //optional: empty if not supported! const SymlinkInfo* symlinkInfo; //only filled if file is a followed symlink }; - struct DirInfo + struct FolderInfo { - const Zstring itemName; + Zstring itemName; + const SymlinkInfo* symlinkInfo; //only filled if folder is a followed symlink }; enum HandleLink { - LINK_FOLLOW, //dereferences link, then calls "onDir()" or "onFile()" + LINK_FOLLOW, //dereferences link, then calls "onFolder()" or "onFile()" LINK_SKIP }; enum HandleError { ON_ERROR_RETRY, - ON_ERROR_IGNORE + ON_ERROR_CONTINUE }; virtual void onFile (const FileInfo& fi) = 0; // virtual HandleLink onSymlink(const SymlinkInfo& si) = 0; //throw X - virtual std::unique_ptr<TraverserCallback> onDir (const DirInfo& di) = 0; // + virtual std::unique_ptr<TraverserCallback> onFolder (const FolderInfo& fi) = 0; // //nullptr: ignore directory, non-nullptr: traverse into, using the (new) callback virtual HandleError reportDirError (const std::wstring& msg, size_t retryNumber) = 0; //failed directory traversal -> consider directory data at current level as incomplete! @@ -217,11 +228,6 @@ struct AbstractFileSystem //THREAD-SAFETY: "const" member functions must model t }; //return current attributes at the time of copy //symlink handling: dereference source - 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)>& notifyProgress) //may be nullptr; throw X! - { return apSource.afs->copyFileAsStream(apSource.itemPathImpl, apTarget, notifyProgress); } - //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: @@ -233,6 +239,7 @@ struct AbstractFileSystem //THREAD-SAFETY: "const" member functions must model t //if target is existing user needs to implement deletion: copyFile() NEVER overwrites target if already existing! //if transactionalCopy == true, full read access on source had been proven at this point, so it's safe to delete it. const std::function<void()>& onDeleteTargetFile, + //accummulated delta != file size! consider ADS, sparse, compressed files const std::function<void(std::int64_t bytesDelta)>& notifyProgress); static void copyNewFolder(const AbstractPath& apSource, const AbstractPath& apTarget, bool copyFilePermissions); //throw FileError @@ -255,7 +262,7 @@ struct AbstractFileSystem //THREAD-SAFETY: "const" member functions must model t //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 + static void recycleItemIfExists(const AbstractPath& ap) { ap.afs->recycleItemIfExists(ap.itemPathImpl); } //throw FileError //================================================================================================================ @@ -268,6 +275,14 @@ 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; } + struct PathDetailsImpl + { + ItemType existingType; + Zstring existingPathImpl; //itemPathImpl =: existingPathImpl + relPath + std::vector<Zstring> relPath; // + }; + PathDetailsImpl getPathDetailsViaFolderTraversal(const Zstring& itemPathImpl) const; //throw FileError + FileAttribAfterCopy copyFileAsStream(const Zstring& itemPathImplSource, const AbstractPath& apTarget, //throw FileError, ErrorTargetExisting, ErrorFileLocked const std::function<void(std::int64_t bytesDelta)>& notifyProgress) const; //may be nullptr; throw X! @@ -286,35 +301,30 @@ private: virtual bool lessItemPathSameAfsType(const Zstring& itemPathImplLhs, const AbstractPath& apRhs) 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 Zstring getItemName(const Zstring& itemPathImpl) const = 0; virtual bool havePathDependencySameAfsType(const Zstring& itemPathImplLhs, const AbstractPath& apRhs) const = 0; //---------------------------------------------------------------------------------------------------------------- - virtual bool fileExists (const Zstring& itemPathImpl) const = 0; //noexcept - virtual bool folderExists (const Zstring& itemPathImpl) const = 0; //noexcept - virtual bool symlinkExists (const Zstring& itemPathImpl) const = 0; //noexcept - virtual bool somethingExists(const Zstring& itemPathImpl) const = 0; //noexcept + virtual ItemType getItemType(const Zstring& itemPathImpl) const = 0; //throw FileError + virtual PathDetailsImpl getPathDetails(const Zstring& itemPathImpl) const = 0; //throw FileError //---------------------------------------------------------------------------------------------------------------- - //should provide for single ATOMIC folder creation! - virtual void createFolderSimple(const Zstring& itemPathImpl) const = 0; //throw FileError, ErrorTargetExisting, ErrorTargetPathMissing + virtual void createFolderPlain(const Zstring& itemPathImpl) const = 0; //throw FileError //non-recursive folder deletion: - virtual void removeFolderSimple(const Zstring& itemPathImpl) const = 0; //throw FileError - - virtual bool removeFile(const Zstring& itemPathImpl) const = 0; //throw FileError - + virtual void removeFilePlain (const Zstring& itemPathImpl) const = 0; //throw FileError + virtual void removeSymlinkPlain(const Zstring& itemPathImpl) const = 0; //throw FileError + virtual void removeFolderPlain (const Zstring& itemPathImpl) const = 0; //throw FileError //---------------------------------------------------------------------------------------------------------------- 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 Zstring getResolvedSymlinkPath(const Zstring& itemPathImpl) const = 0; //throw FileError - virtual Zstring getSymlinkContentBuffer(const Zstring& itemPathImpl) const = 0; //throw FileError + virtual std::string getSymlinkBinaryContent(const Zstring& itemPathImpl) const = 0; //throw FileError //---------------------------------------------------------------------------------------------------------------- virtual std::unique_ptr<InputStream > getInputStream (const Zstring& itemPathImpl) const = 0; //throw FileError, ErrorFileLocked @@ -340,14 +350,13 @@ private: 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 + virtual void recycleItemIfExists(const Zstring& itemPathImpl) const = 0; //throw FileError }; @@ -367,7 +376,7 @@ bool tryReportingDirError(Command cmd, AbstractFileSystem::TraverserCallback& ca { case AbstractFileSystem::TraverserCallback::ON_ERROR_RETRY: break; - case AbstractFileSystem::TraverserCallback::ON_ERROR_IGNORE: + case AbstractFileSystem::TraverserCallback::ON_ERROR_CONTINUE: return false; } } @@ -389,7 +398,7 @@ bool tryReportingItemError(Command cmd, AbstractFileSystem::TraverserCallback& c { case AbstractFileSystem::TraverserCallback::ON_ERROR_RETRY: break; - case AbstractFileSystem::TraverserCallback::ON_ERROR_IGNORE: + case AbstractFileSystem::TraverserCallback::ON_ERROR_CONTINUE: return false; } } @@ -497,7 +506,7 @@ AbstractFileSystem::OutputStream::~OutputStream() outStream_.reset(); //close file handle *before* remove! if (!finalizeSucceeded_) //transactional output stream! => clean up! - try { AbstractFileSystem::removeFile(filePath_); /*throw FileError*/ } + try { AbstractFileSystem::removeFilePlain(filePath_); /*throw FileError*/ } catch (FileError& e) { (void)e; assert(false); } } @@ -545,7 +554,7 @@ void AbstractFileSystem::copyNewFolder(const AbstractPath& apSource, const Abstr throw FileError(replaceCpy(_("Cannot write permissions of %x."), L"%x", fmtPath(getDisplayPath(apTarget))), _("Operation not supported for different base folder types.")); - createFolderSimple(apTarget); //throw FileError, ErrorTargetExisting, ErrorTargetPathMissing + createFolderPlain(apTarget); //throw FileError } diff --git a/FreeFileSync/Source/fs/native.cpp b/FreeFileSync/Source/fs/native.cpp index 9c341769..61d9e0f2 100644 --- a/FreeFileSync/Source/fs/native.cpp +++ b/FreeFileSync/Source/fs/native.cpp @@ -143,10 +143,10 @@ FileAttribs getFileAttributes(FileHandle fh, const Zstring& filePath) //throw Fi struct InputStreamNative : public AbstractFileSystem::InputStream { - InputStreamNative(const Zstring& filePath) : fi(filePath) {} //throw FileError, ErrorFileLocked + InputStreamNative(const Zstring& filePath) : fi_(filePath) {} //throw FileError, ErrorFileLocked - size_t getBlockSize() const override { return fi.getBlockSize(); } //non-zero block size is AFS contract! - size_t tryRead(void* buffer, size_t bytesToRead) override { return fi.tryRead(buffer, bytesToRead); } //throw FileError; may return short, only 0 means EOF! => CONTRACT: bytesToRead > 0 + size_t getBlockSize() const override { return fi_.getBlockSize(); } //non-zero block size is AFS contract! + size_t tryRead(void* buffer, size_t bytesToRead) override { return fi_.tryRead(buffer, bytesToRead); } //throw FileError; may return short, only 0 means EOF! => CONTRACT: bytesToRead > 0 AFS::FileId getFileId () override; //throw FileError std::int64_t getModificationTime() override; //throw FileError std::uint64_t getFileSize () override; //throw FileError @@ -154,13 +154,13 @@ struct InputStreamNative : public AbstractFileSystem::InputStream private: const FileAttribs& getBufferedAttributes() //throw FileError { - if (!fileAttr) - fileAttr = getFileAttributes(fi.getHandle(), fi.getFilePath()); //throw FileError - return *fileAttr; + if (!fileAttr_) + fileAttr_ = getFileAttributes(fi_.getHandle(), fi_.getFilePath()); //throw FileError + return *fileAttr_; } - FileInput fi; - Opt<FileAttribs> fileAttr; + FileInput fi_; + Opt<FileAttribs> fileAttr_; }; @@ -198,31 +198,31 @@ std::uint64_t InputStreamNative::getFileSize() struct OutputStreamNative : public AbstractFileSystem::OutputStreamImpl { OutputStreamNative(const Zstring& filePath, const std::uint64_t* streamSize, const std::int64_t* modTime) : - fo(filePath, FileOutput::ACC_CREATE_NEW) //throw FileError, ErrorTargetExisting + fo_(filePath, FileOutput::ACC_CREATE_NEW) //throw FileError, ErrorTargetExisting { if (modTime) modTime_ = *modTime; if (streamSize) //pre-allocate file space, because we can - preAllocateSpaceBestEffort(fo.getHandle(), *streamSize, fo.getFilePath()); //throw FileError + preAllocateSpaceBestEffort(fo_.getHandle(), *streamSize, fo_.getFilePath()); //throw FileError } - size_t getBlockSize() const override { return fo.getBlockSize(); } //non-zero block size is AFS contract! + size_t getBlockSize() const override { return fo_.getBlockSize(); } //non-zero block size is AFS contract! - size_t tryWrite(const void* buffer, size_t bytesToWrite) override { return fo.tryWrite(buffer, bytesToWrite); } //throw FileError; may return short! CONTRACT: bytesToWrite > 0 + size_t tryWrite(const void* buffer, size_t bytesToWrite) override { return fo_.tryWrite(buffer, bytesToWrite); } //throw FileError; may return short! CONTRACT: bytesToWrite > 0 AFS::FileId finalize(const std::function<void()>& onUpdateStatus) override //throw FileError { - const AFS::FileId fileId = convertToAbstractFileId(extractFileId(getFileAttributes(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 + fo_.close(); //throw FileError if (onUpdateStatus) onUpdateStatus(); //throw X! try { if (modTime_) - zen::setFileTime(fo.getFilePath(), *modTime_, ProcSymlink::FOLLOW); //throw FileError + zen::setFileTime(fo_.getFilePath(), *modTime_, ProcSymlink::FOLLOW); //throw FileError } catch (FileError&) { @@ -235,7 +235,7 @@ struct OutputStreamNative : public AbstractFileSystem::OutputStreamImpl } private: - FileOutput fo; + FileOutput fo_; Opt<std::int64_t> modTime_; }; @@ -243,16 +243,8 @@ private: class NativeFileSystem : public AbstractFileSystem { -public: //itemPathImpl := native full item path as used by OS APIs - static Zstring getItemPathImplForRecycler(const AbstractPath& ap) - { - if (typeid(getAfs(ap)) != typeid(NativeFileSystem)) - throw std::logic_error("Contract violation! " + std::string(__FILE__) + ":" + numberTo<std::string>(__LINE__)); - return getItemPathImpl(ap); - } - private: bool isNativeFileSystem() const override { return true; } @@ -282,33 +274,12 @@ private: bool lessItemPathSameAfsType(const Zstring& itemPathImplLhs, const AbstractPath& apRhs) const override { return LessFilePath()(itemPathImplLhs, getItemPathImpl(apRhs)); } - //used during folder creation if parent folder is missing Opt<Zstring> getParentFolderPathImpl(const Zstring& itemPathImpl) const override { -#ifdef ZEN_WIN - //remove trailing separator (even for C:\ root directories) - const Zstring itemPathFmt = endsWith(itemPathImpl, FILE_NAME_SEPARATOR) ? - beforeLast(itemPathImpl, FILE_NAME_SEPARATOR, IF_MISSING_RETURN_NONE) : - itemPathImpl; - - const Zstring parentDir = beforeLast(itemPathFmt, FILE_NAME_SEPARATOR, IF_MISSING_RETURN_NONE); - if (parentDir.empty()) - return NoValue(); - if (parentDir.size() == 2 && isAlpha(parentDir[0]) && parentDir[1] == L':') - return appendSeparator(parentDir); - -#elif defined ZEN_LINUX || defined ZEN_MAC - if (itemPathImpl == "/") - return NoValue(); - - const Zstring parentDir = beforeLast(itemPathImpl, FILE_NAME_SEPARATOR, IF_MISSING_RETURN_NONE); - if (parentDir.empty()) - return Zstring("/"); -#endif - return parentDir; + return zen::getParentFolderPath(itemPathImpl); } - Zstring getFileShortName(const Zstring& itemPathImpl) const override { return afterLast(itemPathImpl, FILE_NAME_SEPARATOR, IF_MISSING_RETURN_ALL); } + Zstring getItemName(const Zstring& itemPathImpl) const override { return afterLast(itemPathImpl, FILE_NAME_SEPARATOR, IF_MISSING_RETURN_ALL); } bool havePathDependencySameAfsType(const Zstring& itemPathImplLhs, const AbstractPath& apRhs) const override { @@ -321,29 +292,51 @@ private: } //---------------------------------------------------------------------------------------------------------------- - bool fileExists (const Zstring& itemPathImpl) const override { return zen::fileExists (itemPathImpl); } //noexcept - bool folderExists (const Zstring& itemPathImpl) const override { return zen::dirExists (itemPathImpl); } //noexcept - bool symlinkExists (const Zstring& itemPathImpl) const override { return zen::symlinkExists (itemPathImpl); } //noexcept - bool somethingExists(const Zstring& itemPathImpl) const override { return zen::somethingExists(itemPathImpl); } //noexcept + ItemType getItemType(const Zstring& itemPathImpl) const override //throw FileError + { + initComForThread(); //throw FileError + switch (zen::getItemType(itemPathImpl)) //throw FileError + { + case zen::ItemType::FILE: + return AFS::ItemType::FILE; + case zen::ItemType::FOLDER: + return AFS::ItemType::FOLDER; + case zen::ItemType::SYMLINK: + return AFS::ItemType::SYMLINK; + } + assert(false); + return AFS::ItemType::FILE; + } + + PathDetailsImpl getPathDetails(const Zstring& itemPathImpl) const override //throw FileError + { + return getPathDetailsViaFolderTraversal(itemPathImpl); //throw FileError + } //---------------------------------------------------------------------------------------------------------------- //should provide for single ATOMIC folder creation! - void createFolderSimple(const Zstring& itemPathImpl) const override //throw FileError, ErrorTargetExisting, ErrorTargetPathMissing + void createFolderPlain(const Zstring& itemPathImpl) const override //throw FileError { initComForThread(); //throw FileError - copyNewDirectory(Zstring(), itemPathImpl, false /*copyFilePermissions*/); + copyNewDirectory(Zstring(), itemPathImpl, false /*copyFilePermissions*/); //throw FileError, ErrorTargetExisting } - void removeFolderSimple(const Zstring& itemPathImpl) const override //throw FileError + void removeFilePlain(const Zstring& itemPathImpl) const override //throw FileError { initComForThread(); //throw FileError - zen::removeDirectorySimple(itemPathImpl); //throw FileError + zen::removeFilePlain(itemPathImpl); //throw FileError } - bool removeFile(const Zstring& itemPathImpl) const override //throw FileError + void removeSymlinkPlain(const Zstring& itemPathImpl) const override //throw FileError { initComForThread(); //throw FileError - return zen::removeFile(itemPathImpl); //throw FileError + zen::removeSymlinkPlain(itemPathImpl); //throw FileError + } + + void removeFolderPlain(const Zstring& itemPathImpl) const override //throw FileError + { + initComForThread(); //throw FileError + zen::removeDirectoryPlain(itemPathImpl); //throw FileError } //---------------------------------------------------------------------------------------------------------------- @@ -366,10 +359,20 @@ private: return zen::getResolvedSymlinkPath(itemPathImpl); //throw FileError } - Zstring getSymlinkContentBuffer(const Zstring& itemPathImpl) const override //throw FileError + std::string getSymlinkBinaryContent(const Zstring& itemPathImpl) const override //throw FileError { initComForThread(); //throw FileError - return getSymlinkTargetRaw(itemPathImpl); //throw FileError + + std::string content = utfCvrtTo<std::string>(getSymlinkTargetRaw(itemPathImpl)); //throw FileError +#ifdef ZEN_WIN + const DWORD attr = ::GetFileAttributes(applyLongPathPrefix(itemPathImpl).c_str()); + if (attr == INVALID_FILE_ATTRIBUTES) + THROW_LAST_FILE_ERROR(replaceCpy(_("Cannot read file attributes of %x."), L"%x", fmtPath(itemPathImpl)), L"GetFileAttributes"); + + const bool isDirSymlink = (attr & FILE_ATTRIBUTE_DIRECTORY) != 0; + content += static_cast<char>(isDirSymlink); //on Windows there are file and folder symlinks => info belongs to content! +#endif + return content; } //---------------------------------------------------------------------------------------------------------------- @@ -461,14 +464,6 @@ private: catch (FileError&) { assert(false); return ImageHolder(); } } - bool folderExistsThrowing(const Zstring& itemPathImpl) const override //throw FileError - { - //TODO: finish file error detection - - initComForThread(); //throw FileError - return zen::dirExists(itemPathImpl); - } - void connectNetworkFolder(const Zstring& itemPathImpl, bool allowUserInteraction) const override //throw FileError { //TODO: clean-up/remove/re-think connectNetworkFolder() @@ -507,10 +502,10 @@ private: return std::make_unique<RecycleSessionNative>(appendSeparator(itemPathImpl)); } - void recycleItemDirectly(const Zstring& itemPathImpl) const override //throw FileError + void recycleItemIfExists(const Zstring& itemPathImpl) const override //throw FileError { initComForThread(); //throw FileError - zen::recycleOrDelete(itemPathImpl); //throw FileError + zen::recycleOrDeleteIfExists(itemPathImpl); //throw FileError } }; @@ -551,19 +546,14 @@ Zstring RecycleSessionNative::getOrCreateRecyclerTempDirPf() //throw FileError }; */ - //ensure unique ownership: + //ensure unique ownership; base directory should have been created already! Zstring dirpath = baseFolderPathPf_ + Zstr("RecycleBin") + AFS::TEMP_FILE_ENDING; for (int i = 0;; ++i) try { - copyNewDirectory(Zstring(), dirpath, false /*copyFilePermissions*/); //throw FileError, ErrorTargetExisting, ErrorTargetPathMissing + copyNewDirectory(Zstring(), dirpath, false /*copyFilePermissions*/); //throw FileError, ErrorTargetExisting return dirpath; } - catch (ErrorTargetPathMissing&) - { - assert(false); //unexpected: base directory should have been created already! - throw; - } catch (ErrorTargetExisting&) { if (i == 10) throw; //avoid endless recursion in pathological cases @@ -579,69 +569,85 @@ Zstring RecycleSessionNative::getOrCreateRecyclerTempDirPf() //throw FileError bool RecycleSessionNative::recycleItem(const AbstractPath& itemPath, const Zstring& logicalRelPath) //throw FileError { - const Zstring itemPathImpl = NativeFileSystem::getItemPathImplForRecycler(itemPath); assert(!startsWith(logicalRelPath, FILE_NAME_SEPARATOR)); + Opt<Zstring> itemPathNative = AFS::getNativeItemPath(itemPath); + if (!itemPathNative) + throw std::logic_error("Contract violation! " + std::string(__FILE__) + ":" + numberTo<std::string>(__LINE__)); + #ifdef ZEN_WIN - const bool isRemnantRecyclerItem = [&itemPathImpl] //clean-up of recycler temp directory failed during last sync + const bool isRemnantRecyclerItem = [&itemPath] //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 = itemPathImpl.find(L"\\RecycleBin"); - if (pos == Zstring::npos) - return false; + for (AbstractPath path = itemPath;;) + { + Opt<AbstractPath> parentPath = AFS::getParentFolderPath(path); + if (!parentPath) //device root + return false; - auto itEnd = std::find(itemPathImpl.begin() + pos + 1, itemPathImpl.end(), L'\\'); - return endsWith(StringRef<const Zchar>(itemPathImpl.begin(), itEnd), AFS::TEMP_FILE_ENDING); + const Zstring itemName = AFS::getItemName(path); + if (startsWith(itemName, L"RecycleBin") && endsWith(itemName, AFS::TEMP_FILE_ENDING)) + return true; + + path = *parentPath; + } }(); //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 (isRemnantRecyclerItem) - return recycleOrDelete(itemPathImpl); //throw FileError + return recycleOrDeleteIfExists(*itemPathNative); //throw FileError const Zstring tmpPath = getOrCreateRecyclerTempDirPf() + logicalRelPath; //throw FileError - bool deleted = false; auto moveToTempDir = [&] { //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(itemPathImpl, tmpPath); //throw FileError, ErrorDifferentVolume + renameFile(*itemPathNative, tmpPath); //throw FileError, ErrorDifferentVolume, ErrorTargetExisting this->toBeRecycled.push_back(tmpPath); - deleted = true; }; try { try { - moveToTempDir(); //throw FileError, ErrorDifferentVolume + moveToTempDir(); //throw FileError, ErrorDifferentVolume, (ErrorTargetExisting) + return true; } catch (ErrorDifferentVolume&) { throw; } catch (FileError&) { - if (somethingExists(itemPathImpl)) + if (!getItemTypeIfExists(*itemPathNative)) //throw FileError + return false; + + if (Opt<Zstring> parentPath = getParentFolderPath(tmpPath)) { - const Zstring tmpParentDir = beforeLast(tmpPath, FILE_NAME_SEPARATOR, IF_MISSING_RETURN_NONE); //what if C:\ ? - if (!somethingExists(tmpParentDir)) + Opt<PathDetails> pd; + try { pd = getPathDetails(*parentPath); /*throw FileError*/ } + catch (FileError&) {} //previous exception is more relevant + + if (pd && !pd->relPath.empty()) //parent folder not existing { - makeDirectoryRecursively(tmpParentDir); //throw FileError - moveToTempDir(); //throw FileError, ErrorDifferentVolume -> this should work now! + Zstring intermediatePath = pd->existingPath; + for (const Zstring& itemName : pd->relPath) + copyNewDirectory(Zstring(), intermediatePath = appendSeparator(intermediatePath) + itemName, false /*copyFilePermissions*/); //throw FileError, (ErrorTargetExisting) + + //retry: this should work now! + moveToTempDir(); //throw FileError, ErrorDifferentVolume, (ErrorTargetExisting) -> this should work now! + return true; } - else - throw; } + throw; } } - catch (ErrorDifferentVolume&) //MoveFileEx() returns ERROR_PATH_NOT_FOUND *before* considering ERROR_NOT_SAME_DEVICE! => we have to create tmpParentDir to find out! + catch (ErrorDifferentVolume&) //MoveFileEx() returns ERROR_PATH_NOT_FOUND *before* considering ERROR_NOT_SAME_DEVICE! => we have to create tmp parent folder to find out! { - return recycleOrDelete(itemPathImpl); //throw FileError + return recycleOrDeleteIfExists(*itemPathNative); //throw FileError } - return deleted; - #elif defined ZEN_LINUX || defined ZEN_MAC - return recycleOrDelete(itemPathImpl); //throw FileError + return recycleOrDeleteIfExists(*itemPathNative); //throw FileError #endif } @@ -652,14 +658,14 @@ void RecycleSessionNative::tryCleanup(const std::function<void (const std::wstri if (!toBeRecycled.empty()) { //move content of temporary directory to recycle bin in a single call - recycleOrDelete(toBeRecycled, notifyDeletionStatus); //throw FileError + recycleOrDeleteIfExists(toBeRecycled, notifyDeletionStatus); //throw FileError toBeRecycled.clear(); } //clean up temp directory itself (should contain remnant empty directories only) if (!recyclerTmpDir.empty()) { - removeDirectoryRecursively(recyclerTmpDir); //throw FileError + removeDirectoryPlainRecursion(recyclerTmpDir); //throw FileError recyclerTmpDir.clear(); } #endif diff --git a/FreeFileSync/Source/fs/native_traverser_impl.h b/FreeFileSync/Source/fs/native_traverser_impl.h index 35891c8a..d88ffce6 100644 --- a/FreeFileSync/Source/fs/native_traverser_impl.h +++ b/FreeFileSync/Source/fs/native_traverser_impl.h @@ -91,7 +91,7 @@ private: (itemName[1] == 0 || (itemName[1] == '.' && itemName[2] == 0))) continue; - const Zstring& itemPath = appendSeparator(dirPath) + itemName; + const Zstring& itemPath = appendSeparator(dirPath) + itemName; struct ::stat statData = {}; if (!tryReportingItemError([&] @@ -122,7 +122,7 @@ private: { if (S_ISDIR(statDataTrg.st_mode)) //a directory { - if (std::unique_ptr<AFS::TraverserCallback> trav = sink.onDir({ itemName })) + if (std::unique_ptr<AFS::TraverserCallback> trav = sink.onFolder({ itemName, &linkInfo })) traverse(itemPath, *trav); } else //a file or named pipe, ect. @@ -141,7 +141,7 @@ private: } else if (S_ISDIR(statData.st_mode)) //a directory { - if (std::unique_ptr<AFS::TraverserCallback> trav = sink.onDir({ itemName })) + if (std::unique_ptr<AFS::TraverserCallback> trav = sink.onFolder({ itemName, nullptr })) traverse(itemPath, *trav); } else //a file or named pipe, ect. diff --git a/FreeFileSync/Source/lib/db_file.cpp b/FreeFileSync/Source/lib/db_file.cpp index ea679635..c3d1a8cb 100644 --- a/FreeFileSync/Source/lib/db_file.cpp +++ b/FreeFileSync/Source/lib/db_file.cpp @@ -74,7 +74,10 @@ void saveStreams(const DbStreams& streamList, const AbstractPath& dbPath, const writeContainer<ByteArray> (memStreamOut, stream.second); } - assert(!AFS::somethingExists(dbPath)); //orphan tmp files should have been cleaned up at this point! +#ifndef NDEBUG + try { assert(!AFS::getItemTypeIfExists(dbPath)); /*throw FileError*/ } + catch (FileError&) {} //orphan tmp files should have been cleaned up at this point! +#endif //save memory stream to file (as a transaction!) { @@ -134,10 +137,15 @@ DbStreams loadStreams(const AbstractPath& dbPath, const std::function<void(std:: } catch (FileError&) { - if (!AFS::somethingExists(dbPath)) //a benign(?) race condition with FileError + bool dbNotYetExisting = false; + try { dbNotYetExisting = !AFS::getItemTypeIfExists(dbPath); /*throw FileError*/ } + catch (FileError&) {} //previous exception is more relevant + + if (dbNotYetExisting) //throw FileError throw FileErrorDatabaseNotExisting(_("Initial synchronization:") + L" \n" + replaceCpy(_("Database file %x does not yet exist."), L"%x", fmtPath(AFS::getDisplayPath(dbPath)))); - throw; + else + throw; } catch (UnexpectedEndOfStreamError&) { @@ -679,12 +687,12 @@ std::shared_ptr<InSyncFolder> zen::loadLastSynchronousState(const BaseFolderPair const AbstractPath dbPathLeft = getDatabaseFilePath< LEFT_SIDE>(baseFolder); const AbstractPath dbPathRight = getDatabaseFilePath<RIGHT_SIDE>(baseFolder); - if (!baseFolder.isExisting< LEFT_SIDE>() || - !baseFolder.isExisting<RIGHT_SIDE>()) + if (!baseFolder.isAvailable< LEFT_SIDE>() || + !baseFolder.isAvailable<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 AbstractPath filePath = !baseFolder.isExisting<LEFT_SIDE>() ? dbPathLeft : dbPathRight; + const AbstractPath filePath = !baseFolder.isAvailable<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(AFS::getDisplayPath(filePath)))); } @@ -719,10 +727,6 @@ void zen::saveLastSynchronousState(const BaseFolderPair& baseFolder, const std:: 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! - AFS::removeFile(dbPathLeftTmp); // - AFS::removeFile(dbPathRightTmp); //throw FileError - //(try to) load old database files... DbStreams streamsLeft; //list of session ID + DirInfo-stream DbStreams streamsRight; @@ -793,15 +797,19 @@ void zen::saveLastSynchronousState(const BaseFolderPair& baseFolder, const std:: streamsLeft [sessionID] = std::move(updatedStreamLeft); streamsRight[sessionID] = std::move(updatedStreamRight); + //delete old tmp file, if necessary -> throws if deletion fails! + AFS::removeFileIfExists(dbPathLeftTmp); // + AFS::removeFileIfExists(dbPathRightTmp); //throw FileError + //write (temp-) files as a transaction saveStreams(streamsLeft, dbPathLeftTmp, notifyProgress); //throw FileError saveStreams(streamsRight, dbPathRightTmp, notifyProgress); // //operation finished: rename temp files -> this should work (almost) transactionally: //if there were no write access, creation of temp files would have failed - AFS::removeFile(dbPathLeft); //throw FileError + AFS::removeFileIfExists(dbPathLeft); //throw FileError AFS::renameItem(dbPathLeftTmp, dbPathLeft); //throw FileError, (ErrorTargetExisting, ErrorDifferentVolume) - AFS::removeFile(dbPathRight); // + AFS::removeFileIfExists(dbPathRight); // AFS::renameItem(dbPathRightTmp, dbPathRight); // } diff --git a/FreeFileSync/Source/lib/dir_exist_async.h b/FreeFileSync/Source/lib/dir_exist_async.h index d790404e..891ff8aa 100644 --- a/FreeFileSync/Source/lib/dir_exist_async.h +++ b/FreeFileSync/Source/lib/dir_exist_async.h @@ -26,7 +26,7 @@ namespace struct FolderStatus { std::set<AbstractPath, AFS::LessAbstractPath> existing; - std::set<AbstractPath, AFS::LessAbstractPath> missing; + std::set<AbstractPath, AFS::LessAbstractPath> notExisting; std::map<AbstractPath, FileError, AFS::LessAbstractPath> failedChecks; }; @@ -46,7 +46,8 @@ FolderStatus getFolderStatusNonBlocking(const std::set<AbstractPath, AFS::LessAb AFS::connectNetworkFolder(folderPath, allowUserInteraction); //throw FileError //2. check dir existence - return AFS::folderExistsThrowing(folderPath); //throw FileError + return static_cast<bool>(AFS::getItemTypeIfExists(folderPath)); //throw FileError + //TODO: consider ItemType:FILE a failure instead? In any case: return "false" IFF nothing (of any type) exists })); //don't wait (almost) endlessly like Win32 would on non-existing network shares: @@ -70,7 +71,7 @@ FolderStatus getFolderStatusNonBlocking(const std::set<AbstractPath, AFS::LessAb if (fi.second.get()) //throw FileError output.existing.insert(fi.first); else - output.missing.insert(fi.first); + output.notExisting.insert(fi.first); } catch (const FileError& e) { output.failedChecks.emplace(fi.first, e); } } diff --git a/FreeFileSync/Source/lib/dir_lock.cpp b/FreeFileSync/Source/lib/dir_lock.cpp index e2184781..879fe607 100644 --- a/FreeFileSync/Source/lib/dir_lock.cpp +++ b/FreeFileSync/Source/lib/dir_lock.cpp @@ -451,7 +451,7 @@ void waitOnDirLock(const Zstring& lockFilePath, DirLockCallback* callback) //thr if (getFilesize(lockFilePath) != fileSizeOld) //throw FileError continue; //late life sign - removeFile(lockFilePath); //throw FileError + removeFilePlain(lockFilePath); //throw FileError return; } @@ -479,18 +479,23 @@ void waitOnDirLock(const Zstring& lockFilePath, DirLockCallback* callback) //thr } catch (FileError&) { - if (!somethingExists(lockFilePath)) //a benign(?) race condition with FileError - return; //what we are waiting for... + try + { + if (!getItemTypeIfExists(lockFilePath)) //throw FileError + return; //what we are waiting for... + } + catch (FileError&) {} //previous exception is more relevant + throw; } } -void releaseLock(const Zstring& lockFilePath) //throw () +void releaseLock(const Zstring& lockFilePath) //noexcept { try { - removeFile(lockFilePath); //throw FileError + removeFilePlain(lockFilePath); //throw FileError } catch (FileError&) {} } @@ -540,7 +545,8 @@ bool tryLock(const Zstring& lockFilePath) //throw FileError THROW_LAST_FILE_ERROR(replaceCpy(_("Cannot write file %x."), L"%x", fmtPath(lockFilePath)), L"open"); } #endif - ZEN_ON_SCOPE_FAIL( removeFile(lockFilePath); ); + ZEN_ON_SCOPE_FAIL(try { removeFilePlain(lockFilePath); } + catch (FileError&) {}); FileOutput fileOut(fileHandle, lockFilePath); //pass handle ownership //write housekeeping info: user, process info, lock GUID diff --git a/FreeFileSync/Source/lib/ffs_paths.cpp b/FreeFileSync/Source/lib/ffs_paths.cpp index f57409d8..f6d90e58 100644 --- a/FreeFileSync/Source/lib/ffs_paths.cpp +++ b/FreeFileSync/Source/lib/ffs_paths.cpp @@ -44,8 +44,8 @@ Zstring zen::getInstallDirPath() //root install directory WITHOUT path separator bool zen::isPortableVersion() { #ifdef ZEN_WIN - return !fileExists(appendSeparator(getInstallDirPath()) + L"uninstall.exe") && //created by NSIS - !dirExists (appendSeparator(getInstallDirPath()) + L"Uninstall"); //created by Inno Setup + return !fileAvailable(appendSeparator(getInstallDirPath()) + L"uninstall.exe") && //created by NSIS + !dirAvailable (appendSeparator(getInstallDirPath()) + L"Uninstall"); //created by Inno Setup #elif defined ZEN_LINUX return !endsWith(getExecutablePathPf(), "/bin/"); //this check is a bit lame... @@ -96,7 +96,7 @@ Zstring zen::getConfigDirPathPf() Zstring configDirPath = toZ(wxStandardPathsBase::Get().GetUserDataDir()); try { - makeDirectoryRecursively(configDirPath); //throw FileError + createDirectoryIfMissingRecursion(configDirPath); //throw FileError } catch (const FileError&) { assert(false); } diff --git a/FreeFileSync/Source/lib/icon_buffer.cpp b/FreeFileSync/Source/lib/icon_buffer.cpp index 10965397..0e669841 100644 --- a/FreeFileSync/Source/lib/icon_buffer.cpp +++ b/FreeFileSync/Source/lib/icon_buffer.cpp @@ -97,7 +97,7 @@ ImageHolder getDisplayIcon(const AbstractPath& itemPath, IconBuffer::IconSize sz break; } - const Zstring& templateName = AFS::getFileShortName(itemPath); + const Zstring& templateName = AFS::getItemName(itemPath); //2. retrieve file icons #ifdef ZEN_WIN @@ -445,7 +445,7 @@ bool IconBuffer::readyForRetrieval(const AbstractPath& filePath) { #ifdef ZEN_WIN if (iconSizeType == IconBuffer::SIZE_SMALL) - if (hasStandardIconExtension(AFS::getFileShortName(filePath))) + if (hasStandardIconExtension(AFS::getItemName(filePath))) return true; #endif return pimpl->buffer->hasIcon(filePath); @@ -456,7 +456,7 @@ 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 = AFS::getFileShortName(filePath); + const Zstring fileName = AFS::getItemName(filePath); if (iconSizeType == IconBuffer::SIZE_SMALL) //non-thumbnail view, we need file type icons only! if (hasStandardIconExtension(fileName)) return this->getIconByExtension(fileName); //buffered!!! diff --git a/FreeFileSync/Source/lib/parallel_scan.cpp b/FreeFileSync/Source/lib/parallel_scan.cpp index 0c34b534..a0996a9f 100644 --- a/FreeFileSync/Source/lib/parallel_scan.cpp +++ b/FreeFileSync/Source/lib/parallel_scan.cpp @@ -316,7 +316,7 @@ public: level_(level) {} virtual void onFile (const FileInfo& fi) override; // - virtual std::unique_ptr<TraverserCallback> onDir (const DirInfo& di) override; //throw ThreadInterruption + virtual std::unique_ptr<TraverserCallback> onFolder (const FolderInfo& fi) override; //throw ThreadInterruption virtual HandleLink onSymlink(const SymlinkInfo& li) override; // HandleError reportDirError (const std::wstring& msg, size_t retryNumber) override; //throw ThreadInterruption @@ -367,11 +367,11 @@ void DirCallback::onFile(const FileInfo& fi) //throw ThreadInterruption } -std::unique_ptr<AFS::TraverserCallback> DirCallback::onDir(const DirInfo& di) //throw ThreadInterruption +std::unique_ptr<AFS::TraverserCallback> DirCallback::onFolder(const FolderInfo& fi) //throw ThreadInterruption { interruptionPoint(); //throw ThreadInterruption - const Zstring& folderRelPath = parentRelPathPf_ + di.itemName; + const Zstring& folderRelPath = parentRelPathPf_ + fi.itemName; //update status information no matter whether item is excluded or not! if (cfg.acb_.mayReportCurrentFile(cfg.threadID_, cfg.lastReportTime_)) @@ -385,7 +385,7 @@ std::unique_ptr<AFS::TraverserCallback> DirCallback::onDir(const DirInfo& di) // return nullptr; //do NOT traverse subdirs //else: attention! ensure directory filtering is applied later to exclude actually filtered directories - FolderContainer& subFolder = output_.addSubFolder(di.itemName); + FolderContainer& subFolder = output_.addSubFolder(fi.itemName, fi.symlinkInfo != nullptr); if (passFilter) cfg.acb_.incItemsScanned(); //add 1 element to the progress indicator @@ -394,7 +394,7 @@ std::unique_ptr<AFS::TraverserCallback> DirCallback::onDir(const DirInfo& di) // if (!tryReportingItemError([&] //check after FolderContainer::addSubFolder() { throw FileError(replaceCpy(_("Cannot read directory %x."), L"%x", AFS::getDisplayPath(AFS::appendRelPath(cfg.baseFolderPath_, folderRelPath))), L"Endless recursion."); - }, *this, di.itemName)) + }, *this, fi.itemName)) return nullptr; return std::make_unique<DirCallback>(cfg, folderRelPath + FILE_NAME_SEPARATOR, subFolder, level_ + 1); @@ -446,15 +446,15 @@ DirCallback::HandleError DirCallback::reportDirError(const std::wstring& msg, si { switch (cfg.acb_.reportError(msg, retryNumber)) //throw ThreadInterruption { - case FillBufferCallback::ON_ERROR_IGNORE: + case FillBufferCallback::ON_ERROR_CONTINUE: cfg.failedDirReads_[beforeLast(parentRelPathPf_, FILE_NAME_SEPARATOR, IF_MISSING_RETURN_NONE)] = msg; - return ON_ERROR_IGNORE; + return ON_ERROR_CONTINUE; case FillBufferCallback::ON_ERROR_RETRY: return ON_ERROR_RETRY; } assert(false); - return ON_ERROR_IGNORE; + return ON_ERROR_CONTINUE; } @@ -462,15 +462,15 @@ DirCallback::HandleError DirCallback::reportItemError(const std::wstring& msg, s { switch (cfg.acb_.reportError(msg, retryNumber)) //throw ThreadInterruption { - case FillBufferCallback::ON_ERROR_IGNORE: + case FillBufferCallback::ON_ERROR_CONTINUE: cfg.failedItemReads_[parentRelPathPf_ + itemName] = msg; - return ON_ERROR_IGNORE; + return ON_ERROR_CONTINUE; case FillBufferCallback::ON_ERROR_RETRY: return ON_ERROR_RETRY; } assert(false); - return ON_ERROR_IGNORE; + return ON_ERROR_CONTINUE; } //------------------------------------------------------------------------------------------ diff --git a/FreeFileSync/Source/lib/parallel_scan.h b/FreeFileSync/Source/lib/parallel_scan.h index dd720472..901e9037 100644 --- a/FreeFileSync/Source/lib/parallel_scan.h +++ b/FreeFileSync/Source/lib/parallel_scan.h @@ -64,7 +64,7 @@ struct FillBufferCallback enum HandleError { ON_ERROR_RETRY, - ON_ERROR_IGNORE + ON_ERROR_CONTINUE }; virtual HandleError reportError (const std::wstring& msg, size_t retryNumber) = 0; //may throw! virtual void reportStatus(const std::wstring& msg, int itemsTotal ) = 0; // diff --git a/FreeFileSync/Source/lib/parse_plural.h b/FreeFileSync/Source/lib/parse_plural.h index 069a20f9..8c137b0d 100644 --- a/FreeFileSync/Source/lib/parse_plural.h +++ b/FreeFileSync/Source/lib/parse_plural.h @@ -245,21 +245,21 @@ private: using TokenList = std::vector<std::pair<std::string, Token::Type>>; const TokenList tokens { - { "?" , Token::TK_TERNARY_QUEST }, - { ":" , Token::TK_TERNARY_COLON }, + { "?", Token::TK_TERNARY_QUEST }, + { ":", Token::TK_TERNARY_COLON }, { "||", Token::TK_OR }, { "&&", Token::TK_AND }, { "==", Token::TK_EQUAL }, { "!=", Token::TK_NOT_EQUAL }, { "<=", Token::TK_LESS_EQUAL }, - { "<" , Token::TK_LESS }, + { "<", Token::TK_LESS }, { ">=", Token::TK_GREATER_EQUAL }, - { ">" , Token::TK_GREATER }, - { "%" , Token::TK_MODULUS }, - { "n" , Token::TK_VARIABLE_N }, - { "N" , Token::TK_VARIABLE_N }, - { "(" , Token::TK_BRACKET_LEFT }, - { ")" , Token::TK_BRACKET_RIGHT }, + { ">", Token::TK_GREATER }, + { "%", Token::TK_MODULUS }, + { "n", Token::TK_VARIABLE_N }, + { "N", Token::TK_VARIABLE_N }, + { "(", Token::TK_BRACKET_LEFT }, + { ")", Token::TK_BRACKET_RIGHT }, }; const std::string stream_; diff --git a/FreeFileSync/Source/lib/process_xml.cpp b/FreeFileSync/Source/lib/process_xml.cpp index 1e31cabf..13918cb9 100644 --- a/FreeFileSync/Source/lib/process_xml.cpp +++ b/FreeFileSync/Source/lib/process_xml.cpp @@ -85,7 +85,7 @@ XmlGlobalSettings::XmlGlobalSettings() static const wchar_t* winMergeExePath = running64BitWindows() ? L"C:\\Program Files (x86)\\WinMerge\\WinMergeU.exe" : L"C:\\Program Files\\WinMerge\\WinMergeU.exe"; - static const bool winMergeInstalled = fileExists(winMergeExePath); //magic statics! + static const bool winMergeInstalled = fileAvailable(winMergeExePath); //magic statics! if (winMergeInstalled) gui.externelApplications.emplace_back(L"WinMerge", Zstring(L"\"") + winMergeExePath + L"\" \"%local_path%\" \"%local_path2%\""); @@ -722,25 +722,25 @@ bool readStruc(const XmlElement& input, ViewFilterDefault& value) }; XmlIn sharedView = in["Shared"]; - readAttr(sharedView, "Equal" , value.equal); + readAttr(sharedView, "Equal", value.equal); readAttr(sharedView, "Conflict", value.conflict); readAttr(sharedView, "Excluded", value.excluded); XmlIn catView = in["CategoryView"]; - readAttr(catView, "LeftOnly" , value.leftOnly); - readAttr(catView, "RightOnly" , value.rightOnly); - readAttr(catView, "LeftNewer" , value.leftNewer); + readAttr(catView, "LeftOnly", value.leftOnly); + readAttr(catView, "RightOnly", value.rightOnly); + readAttr(catView, "LeftNewer", value.leftNewer); readAttr(catView, "RightNewer", value.rightNewer); - readAttr(catView, "Different" , value.different); + readAttr(catView, "Different", value.different); XmlIn actView = in["ActionView"]; - readAttr(actView, "CreateLeft" , value.createLeft); + readAttr(actView, "CreateLeft", value.createLeft); readAttr(actView, "CreateRight", value.createRight); - readAttr(actView, "UpdateLeft" , value.updateLeft); + readAttr(actView, "UpdateLeft", value.updateLeft); readAttr(actView, "UpdateRight", value.updateRight); - readAttr(actView, "DeleteLeft" , value.deleteLeft); + readAttr(actView, "DeleteLeft", value.deleteLeft); readAttr(actView, "DeleteRight", value.deleteRight); - readAttr(actView, "DoNothing" , value.doNothing); + readAttr(actView, "DoNothing", value.doNothing); return success; //[!] avoid short-circuit evaluation above } @@ -751,25 +751,25 @@ void writeStruc(const ViewFilterDefault& value, XmlElement& output) XmlOut out(output); XmlOut sharedView = out["Shared"]; - sharedView.attribute("Equal" , value.equal); + sharedView.attribute("Equal", value.equal); sharedView.attribute("Conflict", value.conflict); sharedView.attribute("Excluded", value.excluded); XmlOut catView = out["CategoryView"]; - catView.attribute("LeftOnly" , value.leftOnly); - catView.attribute("RightOnly" , value.rightOnly); - catView.attribute("LeftNewer" , value.leftNewer); + catView.attribute("LeftOnly", value.leftOnly); + catView.attribute("RightOnly", value.rightOnly); + catView.attribute("LeftNewer", value.leftNewer); catView.attribute("RightNewer", value.rightNewer); - catView.attribute("Different" , value.different); + catView.attribute("Different", value.different); XmlOut actView = out["ActionView"]; - actView.attribute("CreateLeft" , value.createLeft); + actView.attribute("CreateLeft", value.createLeft); actView.attribute("CreateRight", value.createRight); - actView.attribute("UpdateLeft" , value.updateLeft); + actView.attribute("UpdateLeft", value.updateLeft); actView.attribute("UpdateRight", value.updateRight); - actView.attribute("DeleteLeft" , value.deleteLeft); + actView.attribute("DeleteLeft", value.deleteLeft); actView.attribute("DeleteRight", value.deleteRight); - actView.attribute("DoNothing" , value.doNothing); + actView.attribute("DoNothing", value.doNothing); } } @@ -1046,16 +1046,16 @@ void readConfig(const XmlIn& in, XmlGlobalSettings& config, int formatVer) inGeneral["FailSafeFileCopy" ].attribute("Enabled", config.failSafeFileCopy); inGeneral["CopyLockedFiles" ].attribute("Enabled", config.copyLockedFiles); inGeneral["CopyFilePermissions" ].attribute("Enabled", config.copyFilePermissions); - inGeneral["AutomaticRetry" ].attribute("Count" , config.automaticRetryCount); - inGeneral["AutomaticRetry" ].attribute("Delay" , config.automaticRetryDelay); + inGeneral["AutomaticRetry" ].attribute("Count", config.automaticRetryCount); + inGeneral["AutomaticRetry" ].attribute("Delay", config.automaticRetryDelay); inGeneral["FileTimeTolerance" ].attribute("Seconds", config.fileTimeTolerance); inGeneral["FolderAccessTimeout" ].attribute("Seconds", config.folderAccessTimeout); inGeneral["RunWithBackgroundPriority"].attribute("Enabled", config.runWithBackgroundPriority); inGeneral["LockDirectoriesDuringSync"].attribute("Enabled", config.createLockFile); inGeneral["VerifyCopiedFiles" ].attribute("Enabled", config.verifyFileCopy); - inGeneral["LastSyncsLogSizeMax" ].attribute("Bytes" , config.lastSyncsLogFileSizeMax); + inGeneral["LastSyncsLogSizeMax" ].attribute("Bytes", config.lastSyncsLogFileSizeMax); inGeneral["NotificationSound" ].attribute("CompareFinished", config.soundFileCompareFinished); - inGeneral["NotificationSound" ].attribute("SyncFinished" , config.soundFileSyncFinished); + inGeneral["NotificationSound" ].attribute("SyncFinished", config.soundFileSyncFinished); XmlIn inOpt = inGeneral["OptionalDialogs"]; inOpt["WarnUnresolvedConflicts" ].attribute("Enabled", config.optDialogs.warningUnresolvedConflicts); @@ -1088,8 +1088,8 @@ void readConfig(const XmlIn& in, XmlGlobalSettings& config, int formatVer) XmlIn inCopyToHistory = inCopyTo["FolderHistory"]; inCopyToHistory(config.gui.mainDlg.copyToCfg.folderHistory); - inCopyToHistory.attribute("LastUsedPath" , config.gui.mainDlg.copyToCfg.lastUsedPath); - inCopyToHistory.attribute("MaxSize" , config.gui.mainDlg.copyToCfg.historySizeMax); + inCopyToHistory.attribute("LastUsedPath", config.gui.mainDlg.copyToCfg.lastUsedPath); + inCopyToHistory.attribute("MaxSize", config.gui.mainDlg.copyToCfg.historySizeMax); XmlIn inManualDel = inWnd["ManualDeletion"]; inManualDel.attribute("UseRecycler", config.gui.mainDlg.manualDeletionUseRecycler); @@ -1164,7 +1164,7 @@ void readConfig(const XmlIn& in, XmlGlobalSettings& config, int formatVer) static const wchar_t* winMergeExePath = running64BitWindows() ? L"C:\\Program Files (x86)\\WinMerge\\WinMergeU.exe" : L"C:\\Program Files\\WinMerge\\WinMergeU.exe"; - static const bool winMergeInstalled = fileExists(winMergeExePath); //magic statics! + static const bool winMergeInstalled = fileAvailable(winMergeExePath); //magic statics! if (winMergeInstalled) config.gui.externelApplications.emplace_back(L"WinMerge", Zstring(L"\"") + winMergeExePath + L"\" \"%local_path%\" \"%local_path2%\""); } @@ -1496,16 +1496,16 @@ void writeConfig(const XmlGlobalSettings& config, XmlOut& out) outGeneral["FailSafeFileCopy" ].attribute("Enabled", config.failSafeFileCopy); outGeneral["CopyLockedFiles" ].attribute("Enabled", config.copyLockedFiles); outGeneral["CopyFilePermissions" ].attribute("Enabled", config.copyFilePermissions); - outGeneral["AutomaticRetry" ].attribute("Count" , config.automaticRetryCount); - outGeneral["AutomaticRetry" ].attribute("Delay" , config.automaticRetryDelay); + outGeneral["AutomaticRetry" ].attribute("Count", config.automaticRetryCount); + outGeneral["AutomaticRetry" ].attribute("Delay", config.automaticRetryDelay); outGeneral["FileTimeTolerance" ].attribute("Seconds", config.fileTimeTolerance); outGeneral["FolderAccessTimeout" ].attribute("Seconds", config.folderAccessTimeout); outGeneral["RunWithBackgroundPriority"].attribute("Enabled", config.runWithBackgroundPriority); outGeneral["LockDirectoriesDuringSync"].attribute("Enabled", config.createLockFile); outGeneral["VerifyCopiedFiles" ].attribute("Enabled", config.verifyFileCopy); - outGeneral["LastSyncsLogSizeMax" ].attribute("Bytes" , config.lastSyncsLogFileSizeMax); + outGeneral["LastSyncsLogSizeMax" ].attribute("Bytes", config.lastSyncsLogFileSizeMax); outGeneral["NotificationSound" ].attribute("CompareFinished", config.soundFileCompareFinished); - outGeneral["NotificationSound" ].attribute("SyncFinished" , config.soundFileSyncFinished); + outGeneral["NotificationSound" ].attribute("SyncFinished", config.soundFileSyncFinished); XmlOut outOpt = outGeneral["OptionalDialogs"]; outOpt["WarnUnresolvedConflicts" ].attribute("Enabled", config.optDialogs.warningUnresolvedConflicts); @@ -1538,8 +1538,8 @@ void writeConfig(const XmlGlobalSettings& config, XmlOut& out) XmlOut outCopyToHistory = outCopyTo["FolderHistory"]; outCopyToHistory(config.gui.mainDlg.copyToCfg.folderHistory); - outCopyToHistory.attribute("LastUsedPath" , config.gui.mainDlg.copyToCfg.lastUsedPath); - outCopyToHistory.attribute("MaxSize" , config.gui.mainDlg.copyToCfg.historySizeMax); + outCopyToHistory.attribute("LastUsedPath", config.gui.mainDlg.copyToCfg.lastUsedPath); + outCopyToHistory.attribute("MaxSize", config.gui.mainDlg.copyToCfg.historySizeMax); XmlOut outManualDel = outWnd["ManualDeletion"]; outManualDel.attribute("UseRecycler", config.gui.mainDlg.manualDeletionUseRecycler); diff --git a/FreeFileSync/Source/lib/status_handler.h b/FreeFileSync/Source/lib/status_handler.h index a3616f2c..e050f7fa 100644 --- a/FreeFileSync/Source/lib/status_handler.h +++ b/FreeFileSync/Source/lib/status_handler.h @@ -65,7 +65,7 @@ protected: } void updateProcessedData(int itemsDelta, std::int64_t bytesDelta) override { updateData(numbersCurrent_, itemsDelta, bytesDelta); } //note: these methods MUST NOT throw in order - void updateTotalData (int itemsDelta, std::int64_t bytesDelta) override { updateData(numbersTotal_ , itemsDelta, bytesDelta); } //to properly allow undoing setting of statistics! + void updateTotalData (int itemsDelta, std::int64_t bytesDelta) override { updateData(numbersTotal_, itemsDelta, bytesDelta); } //to properly allow undoing setting of statistics! void requestUiRefresh() override //throw X { @@ -97,10 +97,10 @@ protected: Phase currentPhase() const override { return currentPhase_; } int getItemsCurrent(Phase phaseId) const override { return refNumbers(numbersCurrent_, phaseId).first; } - int getItemsTotal (Phase phaseId) const override { assert(phaseId != PHASE_SCANNING); return refNumbers(numbersTotal_ , phaseId).first; } + int getItemsTotal (Phase phaseId) const override { assert(phaseId != PHASE_SCANNING); return refNumbers(numbersTotal_, phaseId).first; } std::int64_t getBytesCurrent(Phase phaseId) const override { assert(phaseId != PHASE_SCANNING); return refNumbers(numbersCurrent_, phaseId).second; } - std::int64_t getBytesTotal (Phase phaseId) const override { assert(phaseId != PHASE_SCANNING); return refNumbers(numbersTotal_ , phaseId).second; } + std::int64_t getBytesTotal (Phase phaseId) const override { assert(phaseId != PHASE_SCANNING); return refNumbers(numbersTotal_, phaseId).second; } const std::wstring& currentStatusText() const override { return statusText_; } diff --git a/FreeFileSync/Source/lib/versioning.cpp b/FreeFileSync/Source/lib/versioning.cpp index 02b3eca1..df23cb30 100644 --- a/FreeFileSync/Source/lib/versioning.cpp +++ b/FreeFileSync/Source/lib/versioning.cpp @@ -65,8 +65,8 @@ bool impl::isMatchingVersion(const Zstring& shortname, const Zstring& shortnameV /* create target super directories if missing */ -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 +void FileVersioner::moveItemToVersioning(const Zstring& relativePath, //throw FileError + const std::function<void(const AbstractPath& targetPath)>& moveExistingItem) //move source -> target; may throw FileError { assert(!startsWith(relativePath, FILE_NAME_SEPARATOR)); assert(!endsWith (relativePath, FILE_NAME_SEPARATOR)); @@ -88,18 +88,29 @@ void FileVersioner::moveItemToVersioning(const AbstractPath& itemPath, const Zst const AbstractPath versionedItemPath = AFS::appendRelPath(versioningFolderPath_, versionedRelPath); try { - moveItem(itemPath, versionedItemPath); //throw FileError + moveExistingItem(versionedItemPath); //throw FileError } catch (const FileError&) //expected to fail if target directory is not yet existing! { + warn_static("perf: combine with removeTarget!!!") + //create intermediate directories if missing - const AbstractPath versionedParentPath = AFS::appendRelPath(versioningFolderPath_, beforeLast(versionedRelPath, FILE_NAME_SEPARATOR, IF_MISSING_RETURN_NONE)); - if (!AFS::somethingExists(versionedParentPath)) //->(minor) file system race condition! + if (Opt<AbstractPath> parentPath = AFS::getParentFolderPath(versionedItemPath)) { - AFS::createFolderRecursively(versionedParentPath); //throw FileError - //retry: this should work now! - moveItem(itemPath, versionedItemPath); //throw FileError - return; + Opt<AFS::PathDetails> pd; + try { pd = AFS::getPathDetails(*parentPath); /*throw FileError*/ } + catch (FileError&) {} //previous exception is more relevant + + if (pd && !pd->relPath.empty()) //parent folder not existing + { + AbstractPath intermediatePath = pd->existingPath; + for (const Zstring& itemName : pd->relPath) + AFS::createFolderPlain(intermediatePath = AFS::appendRelPath(intermediatePath, itemName)); //throw FileError + + //retry: this should work now! + moveExistingItem(versionedItemPath); //throw FileError + return; + } } throw; } @@ -112,122 +123,101 @@ 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 AbstractPath& sourcePath, //throw FileError - const AbstractPath& targetPath, - Function copyDelete) //throw FileError; fallback if move failed +void moveExistingItem(const AbstractPath& sourcePath, //throw FileError + const AbstractPath& targetPath, + Function copyDelete) //throw FileError; fallback if move failed { - assert(AFS::fileExists(sourcePath) || AFS::symlinkExists(sourcePath) || !AFS::somethingExists(sourcePath)); //we process files and symlinks only - - //first try to move directly without copying - try - { - AFS::renameItem(sourcePath, targetPath); //throw FileError, ErrorTargetExisting, ErrorDifferentVolume - return; //great, we get away cheaply! - } - catch (const FileError&) +#ifndef NDEBUG + try { assert(AFS::getItemType(sourcePath) != AFS::ItemType::FOLDER); /*throw FileError*/ } + catch (FileError&) {} //we process files and symlinks only +#endif + auto removeTarget = [&](bool expectExisting) { - //missing source item is not an error => check BEFORE calling removeTarget()! - if (!AFS::somethingExists(sourcePath)) - return; //object *not* processed - - auto removeTarget = [&] + try + { + //file or (broken) file-symlink: + AFS::removeFilePlain(targetPath); //throw FileError + } + catch (FileError&) { + //(folder-)symlink: + bool symlinkExists = false; try { - //file or (broken) file-symlink: - AFS::removeFile(targetPath); //throw FileError - } - catch (FileError&) - { - //folder or folder-symlink: - if (AFS::folderExists(targetPath)) //directory or dir-symlink + if (expectExisting) //perf, perf, perf, every file access counts! + symlinkExists = AFS::getItemType(targetPath) == AFS::ItemType::SYMLINK; //throw FileError + else { - 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 + if (Opt<AFS::ItemType> type = AFS::getItemTypeIfExists(targetPath)) //throw FileError + symlinkExists = *type == AFS::ItemType::SYMLINK; + else + return; } - else - throw; } - }; + catch (FileError&) {} //previous exception is more relevant - try { throw; } - catch (const ErrorDifferentVolume&) + if (symlinkExists) + AFS::removeSymlinkPlain(targetPath); //throw FileError + else //overwrite AFS::ItemType::FOLDER with FILE? => highly dubious, do not allow + throw; + } + }; + + try //first try to move directly without copying + { + AFS::renameItem(sourcePath, targetPath); //throw FileError, ErrorTargetExisting, ErrorDifferentVolume + //great, we get away cheaply! + } + catch (const ErrorDifferentVolume&) + { + removeTarget(false /*expectExisting*/); //throw FileError + copyDelete(); // + } + catch (const ErrorTargetExisting&) + { + removeTarget(true /*expectExisting*/); //throw FileError + try { - removeTarget(); //throw FileError - copyDelete(); // + AFS::renameItem(sourcePath, targetPath); //throw FileError, (ErrorTargetExisting), ErrorDifferentVolume } - catch (const ErrorTargetExisting&) + catch (const ErrorDifferentVolume&) { - removeTarget(); //throw FileError - try - { - AFS::renameItem(sourcePath, targetPath); //throw FileError, (ErrorTargetExisting), ErrorDifferentVolume - } - catch (const ErrorDifferentVolume&) - { - copyDelete(); //throw FileError - } + copyDelete(); //throw FileError } } } -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(!AFS::somethingExists(targetPath)); - if (AFS::symlinkExists(sourcePath)) - AFS::copySymlink(sourcePath, targetPath, false /*copy filesystem permissions*/); //throw FileError - else - AFS::copyFileTransactional(sourcePath, targetPath, //throw FileError, ErrorFileLocked - false /*copyFilePermissions*/, true /*transactionalCopy*/, nullptr /*onDeleteTargetFile*/, onNotifyCopyStatus); - - 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 AbstractPath& sourcePath, //throw FileError - const AbstractPath& targetPath, - const std::function<void(std::int64_t bytesDelta)>& onNotifyCopyStatus) //may be nullptr +void moveExistingFile(const AbstractPath& sourcePath, //throw FileError + const AbstractPath& targetPath, + const std::function<void(std::int64_t bytesDelta)>& onNotifyCopyStatus) //may be nullptr { auto copyDelete = [&] { - assert(!AFS::somethingExists(targetPath)); +#ifndef NDEBUG + try { assert(!AFS::getItemTypeIfExists(targetPath)); /*throw FileError*/ } + catch (FileError&) {} +#endif AFS::copyFileTransactional(sourcePath, targetPath, //throw FileError, ErrorFileLocked false /*copyFilePermissions*/, true /*transactionalCopy*/, nullptr /*onDeleteTargetFile*/, onNotifyCopyStatus); - AFS::removeFile(sourcePath); //throw FileError; newly copied file is NOT deleted if exception is thrown here! + AFS::removeFilePlain(sourcePath); //throw FileError; newly copied item is NOT deleted if exception is thrown here! }; - moveItem(sourcePath, targetPath, copyDelete); //throw FileError + moveExistingItem(sourcePath, targetPath, copyDelete); //throw FileError } -void moveFileSymlink(const AbstractPath& sourcePath, const AbstractPath& targetPath) //throw FileError +void moveExistingSymlink(const AbstractPath& sourcePath, const AbstractPath& targetPath) //throw FileError { auto copyDelete = [&] { - 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 AbstractPath& sourcePath, const AbstractPath& targetPath) //throw FileError -{ - auto copyDelete = [&] //throw FileError - { - assert(!AFS::somethingExists(targetPath)); +#ifndef NDEBUG + try { assert(!AFS::getItemTypeIfExists(targetPath)); /*throw FileError*/ } + catch (FileError&) {} +#endif 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! + AFS::removeSymlinkPlain(sourcePath); //throw FileError; newly copied item is NOT deleted if exception is thrown here! }; - moveItem(sourcePath, targetPath, copyDelete); + moveExistingItem(sourcePath, targetPath, copyDelete); //throw FileError } @@ -235,45 +225,65 @@ struct FlatTraverserCallback: public AFS::TraverserCallback { FlatTraverserCallback(const AbstractPath& folderPath) : folderPath_(folderPath) {} - const std::vector<Zstring>& refFileNames () const { return fileNames_; } - const std::vector<Zstring>& refFolderNames () const { return folderNames_; } - const std::vector<Zstring>& refFileLinkNames () const { return fileLinkNames_; } - const std::vector<Zstring>& refFolderLinkNames() const { return folderLinkNames_; } + const std::vector<Zstring>& refFileNames () const { return fileNames_; } + const std::vector<Zstring>& refFolderNames () const { return folderNames_; } + const std::vector<Zstring>& refSymlinkNames() const { return symlinkNames_; } private: - 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 (AFS::folderExists(AFS::appendRelPath(folderPath_, si.itemName))) //dir symlink - folderLinkNames_.push_back(si.itemName); - else //file symlink, broken symlink - fileLinkNames_.push_back(si.itemName); - return TraverserCallback::LINK_SKIP; - } + void onFile (const FileInfo& fi) override { fileNames_ .push_back(fi.itemName); } + std::unique_ptr<TraverserCallback> onFolder (const FolderInfo& fi) override { folderNames_ .push_back(fi.itemName); return nullptr; } + HandleLink onSymlink(const SymlinkInfo& si) override { symlinkNames_.push_back(si.itemName); return TraverserCallback::LINK_SKIP; } + 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 AbstractPath folderPath_; std::vector<Zstring> fileNames_; std::vector<Zstring> folderNames_; - std::vector<Zstring> fileLinkNames_; - std::vector<Zstring> folderLinkNames_; + std::vector<Zstring> symlinkNames_; }; } bool FileVersioner::revisionFile(const AbstractPath& filePath, const Zstring& relativePath, const std::function<void(std::int64_t bytesDelta)>& onNotifyCopyStatus) //throw FileError { - bool moveSuccessful = false; + if (Opt<AFS::ItemType> type = AFS::getItemTypeIfExists(filePath)) //throw FileError + { + bool moveSuccessful = false; - moveItemToVersioning(filePath, relativePath, //throw FileError - [&](const AbstractPath& sourcePath, const AbstractPath& targetPath) + moveItemToVersioning(relativePath, //throw FileError + [&](const AbstractPath& targetPath) + { + if (*type == AFS::ItemType::SYMLINK) + moveExistingSymlink(filePath, targetPath); //throw FileError + else + moveExistingFile(filePath, targetPath, onNotifyCopyStatus); //throw FileError + + moveSuccessful = true; + }); + return moveSuccessful; + } + else + return false; //missing source item is not an error => check BEFORE oerwriting target +} + + +bool FileVersioner::revisionSymlink(const AbstractPath& linkPath, const Zstring& relativePath) //throw FileError +{ + if (AFS::getItemTypeIfExists(linkPath)) //throw FileError { - moveFileOrSymlink(sourcePath, targetPath, onNotifyCopyStatus); //throw FileError - moveSuccessful = true; - }); - return moveSuccessful; + bool moveSuccessful = false; + + moveItemToVersioning(relativePath, //throw FileError + [&](const AbstractPath& targetPath) + { + moveExistingSymlink(linkPath, targetPath); //throw FileError + moveSuccessful = true; + }); + return moveSuccessful; + } + else + return false; } @@ -282,22 +292,22 @@ void FileVersioner::revisionFolder(const AbstractPath& folderPath, const Zstring const std::function<void(const std::wstring& displayPathFrom, const std::wstring& displayPathTo)>& onBeforeFolderMove, const std::function<void(std::int64_t bytesDelta)>& onNotifyCopyStatus) { - 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! + if (Opt<AFS::ItemType> type = AFS::getItemTypeIfExists(folderPath)) //throw FileError { - moveItemToVersioning(folderPath, relativePath, //throw FileError - [&](const AbstractPath& sourcePath, const AbstractPath& targetPath) + if (*type == AFS::ItemType::SYMLINK) //on Linux there is just one type of symlink, and since we do revision file symlinks, we should revision dir symlinks as well! { - if (onBeforeFolderMove) - 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 (AFS::somethingExists(folderPath)) + moveItemToVersioning(relativePath, //throw FileError + [&](const AbstractPath& targetPath) + { + if (onBeforeFileMove) + onBeforeFileMove(AFS::getDisplayPath(folderPath), AFS::getDisplayPath(targetPath)); + moveExistingSymlink(folderPath, targetPath); //throw FileError + }); + } + else revisionFolderImpl(folderPath, relativePath, onBeforeFileMove, onBeforeFolderMove, onNotifyCopyStatus); //throw FileError } + //no error situation if directory is not existing! manual deletion relies on it! } @@ -306,8 +316,10 @@ void FileVersioner::revisionFolderImpl(const AbstractPath& folderPath, const Zst const std::function<void(const std::wstring& displayPathFrom, const std::wstring& displayPathTo)>& onBeforeFolderMove, const std::function<void(std::int64_t bytesDelta)>& onNotifyCopyStatus) { - assert(!AFS::symlinkExists(folderPath)); //[!] no symlinks in this context!!! - assert(AFS::folderExists(folderPath)); //Do NOT traverse into it deleting contained files!!! +#ifndef NDEBUG //[!] no symlinks in this context!!! Do NOT traverse into it deleting contained files!!! + try { assert(AFS::getItemType(folderPath) != AFS::ItemType::SYMLINK); /*throw FileError*/ } + catch (FileError&) {} +#endif //create target directories only when needed in moveFileToVersioning(): avoid empty directories! @@ -317,34 +329,23 @@ void FileVersioner::revisionFolderImpl(const AbstractPath& folderPath, const Zst const Zstring relPathPf = appendSeparator(relativePath); for (const Zstring& fileName : ft.refFileNames()) - moveItemToVersioning(AFS::appendRelPath(folderPath, fileName), //throw FileError - relPathPf + fileName, - [&](const AbstractPath& sourcePath, const AbstractPath& targetPath) + moveItemToVersioning(relPathPf + fileName, //throw FileError + [&](const AbstractPath& targetPath) { + const AbstractPath sourcePath = AFS::appendRelPath(folderPath, fileName); if (onBeforeFileMove) onBeforeFileMove(AFS::getDisplayPath(sourcePath), AFS::getDisplayPath(targetPath)); - moveFile(sourcePath, targetPath, onNotifyCopyStatus); //throw FileError + moveExistingFile(sourcePath, targetPath, onNotifyCopyStatus); //throw FileError }); - for (const Zstring& fileLinkName : ft.refFileLinkNames()) - moveItemToVersioning(AFS::appendRelPath(folderPath, fileLinkName), //throw FileError - relPathPf + fileLinkName, - [&](const AbstractPath& sourcePath, const AbstractPath& targetPath) + for (const Zstring& symlinkName : ft.refSymlinkNames()) + moveItemToVersioning(relPathPf + symlinkName, //throw FileError + [&](const AbstractPath& targetPath) { + const AbstractPath sourcePath = AFS::appendRelPath(folderPath, symlinkName); if (onBeforeFileMove) 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(AFS::appendRelPath(folderPath, folderLinkName), //throw FileError - relPathPf + folderLinkName, - [&](const AbstractPath& sourcePath, const AbstractPath& targetPath) - { - if (onBeforeFolderMove) - onBeforeFolderMove(AFS::getDisplayPath(sourcePath), AFS::getDisplayPath(targetPath)); - moveFolderSymlink(sourcePath, targetPath); //throw FileError + moveExistingSymlink(sourcePath, targetPath); //throw FileError }); //move folders recursively @@ -356,7 +357,7 @@ void FileVersioner::revisionFolderImpl(const AbstractPath& folderPath, const Zst if (onBeforeFolderMove) onBeforeFolderMove(AFS::getDisplayPath(folderPath), AFS::getDisplayPath(AFS::appendRelPath(versioningFolderPath_, relativePath))); - AFS::removeFolderSimple(folderPath); //throw FileError + AFS::removeFolderPlain(folderPath); //throw FileError } diff --git a/FreeFileSync/Source/lib/versioning.h b/FreeFileSync/Source/lib/versioning.h index 82bf9531..821c3b18 100644 --- a/FreeFileSync/Source/lib/versioning.h +++ b/FreeFileSync/Source/lib/versioning.h @@ -47,10 +47,11 @@ public: 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 + bool revisionSymlink(const AbstractPath& linkPath, const Zstring& relativePath); //throw FileError; return "false" if file is not existing + void revisionFolder(const AbstractPath& folderPath, const Zstring& relativePath, //throw FileError //optional callbacks: may be nullptr @@ -67,9 +68,8 @@ private: 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 AbstractPath& itemPath, //throw FileError - const Zstring& relativePath, - const std::function<void(const AbstractPath& sourcePath, const AbstractPath& targetPath)>& moveItem); //may throw FileError + void moveItemToVersioning(const Zstring& relativePath, //throw FileError + const std::function<void(const AbstractPath& targetPath)>& moveItem); //may throw FileError const AbstractPath versioningFolderPath_; const VersioningStyle versioningStyle_; diff --git a/FreeFileSync/Source/synchronization.cpp b/FreeFileSync/Source/synchronization.cpp index 4f36ded1..bc8fc6fd 100644 --- a/FreeFileSync/Source/synchronization.cpp +++ b/FreeFileSync/Source/synchronization.cpp @@ -353,7 +353,7 @@ public: 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); // + template <class Function> void removeLinkWithCallback (const AbstractPath& linkPath, const Zstring& relativePath, Function onNotifyItemDeletion); // const std::wstring& getTxtRemovingFile () const { return txtRemovingFile_; } // const std::wstring& getTxtRemovingFolder () const { return txtRemovingFolder_; } //buffered status texts @@ -500,12 +500,12 @@ void DeletionHandling::removeDirWithCallback(const AbstractPath& folderPath, auto onBeforeFileDeletion = [&](const std::wstring& displayPath) { notifyDeletion(txtRemovingFile_, displayPath); }; auto onBeforeDirDeletion = [&](const std::wstring& displayPath) { notifyDeletion(txtRemovingFolder_, displayPath); }; - AFS::removeFolderRecursively(folderPath, onBeforeFileDeletion, onBeforeDirDeletion); //throw FileError + AFS::removeFolderIfExistsRecursion(folderPath, onBeforeFileDeletion, onBeforeDirDeletion); //throw FileError } break; case DeletionPolicy::RECYCLER: - if (getOrCreateRecyclerSession().recycleItem(folderPath, relativePath)) //throw FileError; return true if item existed + if (getOrCreateRecyclerSession().recycleItem(folderPath, relativePath)) //throw FileError onNotifyItemDeletion(); //moving to recycler is ONE logical operation, irrespective of the number of child elements! break; @@ -535,18 +535,16 @@ void DeletionHandling::removeFileWithCallback(const AbstractPath& filePath, bool deleted = false; if (endsWith(relativePath, AFS::TEMP_FILE_ENDING)) //special rule for .ffs_tmp files: always delete permanently! - deleted = AFS::removeFile(filePath); //throw FileError + deleted = AFS::removeFileIfExists(filePath); //throw FileError else switch (deletionPolicy_) { case DeletionPolicy::PERMANENT: - deleted = AFS::removeFile(filePath); //throw FileError + deleted = AFS::removeFileIfExists(filePath); //throw FileError break; - case DeletionPolicy::RECYCLER: - deleted = getOrCreateRecyclerSession().recycleItem(filePath, relativePath); //throw FileError; return true if item existed + deleted = getOrCreateRecyclerSession().recycleItem(filePath, relativePath); //throw FileError break; - case DeletionPolicy::VERSIONING: deleted = getOrCreateVersioner().revisionFile(filePath, relativePath, onNotifyCopyStatus); //throw FileError break; @@ -557,12 +555,24 @@ void DeletionHandling::removeFileWithCallback(const AbstractPath& filePath, template <class Function> inline -void DeletionHandling::removeLinkWithCallback(const AbstractPath& 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) //throw FileError { - 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 + bool deleted = false; + + switch (deletionPolicy_) + { + case DeletionPolicy::PERMANENT: + deleted = AFS::removeSymlinkIfExists(linkPath); //throw FileError + break; + case DeletionPolicy::RECYCLER: + deleted = getOrCreateRecyclerSession().recycleItem(linkPath, relativePath); //throw FileError + break; + case DeletionPolicy::VERSIONING: + deleted = getOrCreateVersioner().revisionSymlink(linkPath, relativePath); //throw FileError + break; + } + if (deleted) + onNotifyItemDeletion(); } //------------------------------------------------------------------------------------------------------------ @@ -1183,13 +1193,14 @@ void SynchronizeFolderPair::synchronizeFileInt(FilePair& file, SyncOperation syn } catch (FileError&) { - if (AFS::somethingExists(file.getAbstractPath<sideSrc>())) //do not check on type (symlink, file, folder) -> if there is a type change, FFS should error out! - throw; - - //TODO: failure to access base dir is incorrectly considered an empty dir + bool sourceWasDeleted = false; + try { sourceWasDeleted = !AFS::getItemTypeIfExists(file.getAbstractPath<sideSrc>()); /*throw FileError*/ } + catch (FileError&) {} //previous exception is more relevant - //source deleted meanwhile...nothing was done (logical point of view!) - file.removeObject<sideSrc>(); //remove only *after* evaluating "file, sideSrc"! + if (sourceWasDeleted) + file.removeObject<sideSrc>(); //source deleted meanwhile...nothing was done (logical point of view!) + else + throw; //do not check on type (symlink, file, folder) -> if there is a type change, FFS should not be quiet about it! } } break; @@ -1394,13 +1405,14 @@ void SynchronizeFolderPair::synchronizeLinkInt(SymlinkPair& symlink, SyncOperati } catch (FileError&) { - 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; - - //TODO: failure to access base dir is incorrectly considered an empty dir + bool sourceWasDeleted = false; + try { sourceWasDeleted = !AFS::getItemTypeIfExists(symlink.getAbstractPath<sideSrc>()); /*throw FileError*/ } + catch (FileError&) {} //previous exception is more relevant - //source deleted meanwhile...nothing was done (logical point of view!) - symlink.removeObject<sideSrc>(); + if (sourceWasDeleted) + symlink.removeObject<sideSrc>(); //source deleted meanwhile...nothing was done (logical point of view!) + else + throw; //do not check on type (symlink, file, folder) -> if there is a type change, FFS should not be quiet about it! } } break; @@ -1412,9 +1424,8 @@ void SynchronizeFolderPair::synchronizeLinkInt(SymlinkPair& symlink, SyncOperati StatisticsReporter statReporter(1, 0, procCallback_); auto onNotifyItemDeletion = [&] { statReporter.reportDelta(1, 0); }; - auto onNotifyCopyStatus = [&](std::int64_t bytesDelta) { statReporter.reportDelta(0, bytesDelta); }; - getDelHandling<sideTrg>().removeLinkWithCallback(symlink.getAbstractPath<sideTrg>(), symlink.getPairRelativePath(), onNotifyItemDeletion, onNotifyCopyStatus); //throw FileError + getDelHandling<sideTrg>().removeLinkWithCallback(symlink.getAbstractPath<sideTrg>(), symlink.getPairRelativePath(), onNotifyItemDeletion); //throw FileError //TODO: failure to access item during deletion is an error and should not be ignored! @@ -1428,10 +1439,8 @@ void SynchronizeFolderPair::synchronizeLinkInt(SymlinkPair& symlink, SyncOperati { StatisticsReporter statReporter(1, 0, procCallback_); - auto onNotifyCopyStatus = [&](std::int64_t bytesDelta) { statReporter.reportDelta(0, bytesDelta); }; - //reportStatus(getDelHandling<sideTrg>().getTxtRemovingSymLink(), AFS::getDisplayPath(symlink.getAbstractPath<sideTrg>())); - getDelHandling<sideTrg>().removeLinkWithCallback(symlink.getAbstractPath<sideTrg>(), symlink.getPairRelativePath(), [] {}, onNotifyCopyStatus); //throw FileError + getDelHandling<sideTrg>().removeLinkWithCallback(symlink.getAbstractPath<sideTrg>(), symlink.getPairRelativePath(), [] {}); //throw FileError //symlink.removeObject<sideTrg>(); -> "symlink, sideTrg" evaluated below! @@ -1522,24 +1531,32 @@ void SynchronizeFolderPair::synchronizeFolderInt(FolderPair& folder, SyncOperati const AbstractPath targetPath = AFS::appendRelPath(folder.base().getAbstractPath<sideTrg>(), folder.getRelativePath<sideSrc>()); reportInfo(txtCreatingFolder, AFS::getDisplayPath(targetPath)); - if (AFS::somethingExists(folder.getAbstractPath<sideSrc>())) //do not check on type (symlink, file, folder) -> if there is a type change, FFS should error out! + //shallow-"copying" a folder might not fail if source is missing, so we need to check this first: + if (AFS::getItemTypeIfExists(folder.getAbstractPath<sideSrc>())) //throw FileError { StatisticsReporter statReporter(1, 0, procCallback_); try { AFS::copyNewFolder(folder.getAbstractPath<sideSrc>(), targetPath, copyFilePermissions_); //throw FileError } - catch (const FileError&) { if (!AFS::folderExists(targetPath)) throw; } + catch (FileError&) + { + bool folderAlreadyExists = false; + try { folderAlreadyExists = AFS::getItemType(targetPath) == AFS::ItemType::FOLDER; } /*throw FileError*/ catch (FileError&) {} //previous exception is more relevant + + if (!folderAlreadyExists) + throw; + } statReporter.reportDelta(1, 0); //update FolderPair - folder.setSyncedTo(folder.getItemName<sideSrc>()); + folder.setSyncedTo<sideTrg>(folder.getItemName<sideSrc>(), + false, //isSymlinkTrg + folder.isFollowedSymlink<sideSrc>()); } - else //source deleted meanwhile...nothing was done (logical point of view!) -> uh....what about a temporary network drop??? + else //source deleted meanwhile...nothing was done (logical point of view!) { - //TODO: failure to access base dir is incorrectly considered an empty dir - const SyncStatistics subStats(folder); StatisticsReporter statReporter(1 + getCUD(subStats), subStats.getBytesToProcess(), procCallback_); @@ -1590,7 +1607,9 @@ void SynchronizeFolderPair::synchronizeFolderInt(FolderPair& folder, SyncOperati statReporter.reportDelta(1, 0); //-> both sides *should* be completely equal now... - folder.setSyncedTo(folder.getItemName<sideSrc>()); + folder.setSyncedTo<sideTrg>(folder.getItemName<sideSrc>(), + folder.isFollowedSymlink<sideTrg>(), + folder.isFollowedSymlink<sideSrc>()); } break; @@ -1711,7 +1730,8 @@ AFS::FileAttribAfterCopy SynchronizeFolderPair::copyFileWithCallback(const Abstr //#################### Verification ############################# if (verifyCopiedFiles_) { - ZEN_ON_SCOPE_FAIL( AFS::removeFile(targetPath); ); //delete target if verification fails + ZEN_ON_SCOPE_FAIL(try { AFS::removeFilePlain(targetPath); } + catch (FileError&) {}); //delete target if verification fails procCallback_.reportInfo(replaceCpy(txtVerifying, L"%x", fmtPath(AFS::getDisplayPath(targetPath)))); verifyFiles(sourcePathTmp, targetPath, [&](std::int64_t bytesDelta) { procCallback_.requestUiRefresh(); }); //throw FileError @@ -1763,7 +1783,7 @@ bool baseFolderDrop(BaseFolderPair& baseFolder, int folderAccessTimeout, Process { const AbstractPath folderPath = baseFolder.getAbstractPath<side>(); - if (baseFolder.isExisting<side>()) + if (baseFolder.isAvailable<side>()) if (Opt<std::wstring> errMsg = tryReportingError([&] { const FolderStatus status = getFolderStatusNonBlocking({ folderPath }, folderAccessTimeout, false /*allowUserInteraction*/, callback); @@ -1783,30 +1803,30 @@ bool baseFolderDrop(BaseFolderPair& baseFolder, int folderAccessTimeout, Process template <SelectedSide side> //create base directories first (if not yet existing) -> no symlink or attribute copying! -bool createBaseFolder(BaseFolderPair& baseFolder, ProcessCallback& callback) //nothrow; return false if fatal error occurred +bool createBaseFolder(BaseFolderPair& baseFolder, int folderAccessTimeout, ProcessCallback& callback) //nothrow; return false if fatal error occurred { const AbstractPath baseFolderPath = baseFolder.getAbstractPath<side>(); if (AFS::isNullPath(baseFolderPath)) return true; - if (!baseFolder.isExisting<side>()) //create target directory: user presumably ignored error "dir existing" in order to have it created automatically + if (!baseFolder.isAvailable<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([&] { - try - { - //a nice race-free check and set operation: - AFS::createFolderSimple(baseFolderPath); //throw FileError, ErrorTargetExisting, ErrorTargetPathMissing - baseFolder.setExisting<side>(true); //update our model! - } - catch (ErrorTargetPathMissing&) + const FolderStatus status = getFolderStatusNonBlocking({ baseFolderPath }, folderAccessTimeout, false /*allowUserInteraction*/, callback); + + static_assert(IsSameType<decltype(status.failedChecks.begin()->second), FileError>::value, ""); + if (!status.failedChecks.empty()) + throw status.failedChecks.begin()->second; + + if (status.notExisting.find(baseFolderPath) != status.notExisting.end()) { - AFS::createFolderRecursively(baseFolderPath); //throw FileError - baseFolder.setExisting<side>(true); //update our model! + AFS::createFolderIfMissingRecursion(baseFolderPath); //throw FileError + baseFolder.setAvailable<side>(true); //update our model! } - catch (ErrorTargetExisting&) + else { //TEMPORARY network drop! base directory not found during comparison, but reappears during synchronization //=> sync-directions are based on false assumptions! Abort. @@ -1821,7 +1841,6 @@ bool createBaseFolder(BaseFolderPair& baseFolder, ProcessCallback& callback) //n }, callback); //throw X? return !errMsg && !temporaryNetworkDrop; } - return true; } @@ -2021,22 +2040,22 @@ void zen::synchronize(const TimeComp& timeStamp, } //allow propagation of deletions only from *null-* or *existing* source folder: - auto sourceFolderMissing = [&](const AbstractPath& baseFolderPath, bool wasExisting) //we need to evaluate existence status from time of comparison! + auto sourceFolderMissing = [&](const AbstractPath& baseFolderPath, bool wasAvailable) //we need to evaluate existence status from time of comparison! { if (!AFS::isNullPath(baseFolderPath)) //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.deleteCount() > 0) //check deletions only... (respect filtered items!) //folderPairStat.conflictCount() == 0 && -> there COULD be conflicts for <Two way> variant 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! + if (!wasAvailable) //avoid race-condition: we need to evaluate existence status from time of comparison! { callback.reportFatalError(replaceCpy(_("Source folder %x not found."), L"%x", fmtPath(AFS::getDisplayPath(baseFolderPath)))); return true; } return false; }; - if (sourceFolderMissing(baseFolder.getAbstractPath< LEFT_SIDE>(), baseFolder.isExisting< LEFT_SIDE>()) || - sourceFolderMissing(baseFolder.getAbstractPath<RIGHT_SIDE>(), baseFolder.isExisting<RIGHT_SIDE>())) + if (sourceFolderMissing(baseFolder.getAbstractPath< LEFT_SIDE>(), baseFolder.isAvailable< LEFT_SIDE>()) || + sourceFolderMissing(baseFolder.getAbstractPath<RIGHT_SIDE>(), baseFolder.isAvailable<RIGHT_SIDE>())) { jobType[folderIndex] = FolderPairJobType::SKIP; continue; @@ -2202,15 +2221,15 @@ void zen::synchronize(const TimeComp& timeStamp, L" " + AFS::getDisplayPath(baseFolder.getAbstractPath<RIGHT_SIDE>())); //------------------------------------------------------------------------------------------ - //checking a second time: (a long time may have passed since the intro checks!) + //checking a second time: (a long time may have passed since folder comparison!) if (baseFolderDrop< LEFT_SIDE>(baseFolder, folderAccessTimeout, callback) || baseFolderDrop<RIGHT_SIDE>(baseFolder, folderAccessTimeout, callback)) continue; //create base folders if not yet existing if (folderPairStat.createCount() > 0 || folderPairCfg.saveSyncDB_) //else: temporary network drop leading to deletions already caught by "sourceFolderMissing" check! - if (!createBaseFolder< LEFT_SIDE>(baseFolder, callback) || //+ detect temporary network drop!! - !createBaseFolder<RIGHT_SIDE>(baseFolder, callback)) // + if (!createBaseFolder< LEFT_SIDE>(baseFolder, folderAccessTimeout, callback) || //+ detect temporary network drop!! + !createBaseFolder<RIGHT_SIDE>(baseFolder, folderAccessTimeout, callback)) // continue; //------------------------------------------------------------------------------------------ diff --git a/FreeFileSync/Source/ui/batch_status_handler.cpp b/FreeFileSync/Source/ui/batch_status_handler.cpp index 76fe7b10..3d56ad7c 100644 --- a/FreeFileSync/Source/ui/batch_status_handler.cpp +++ b/FreeFileSync/Source/ui/batch_status_handler.cpp @@ -33,7 +33,7 @@ std::pair<std::unique_ptr<AFS::OutputStream>, AbstractPath> prepareNewLogfile(co assert(!jobName.empty()); //create logfile folder if required - AFS::createFolderRecursively(logFolderPath); //throw FileError + AFS::createFolderIfMissingRecursion(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://www.freefilesync.org/forum/viewtopic.php?t=1679 @@ -76,11 +76,11 @@ struct LogTraverserCallback: public AFS::TraverserCallback if (onUpdateStatus_) onUpdateStatus_(); } - std::unique_ptr<TraverserCallback> onDir (const DirInfo& di) override { return nullptr; } + std::unique_ptr<TraverserCallback> onFolder (const FolderInfo& fi) override { return nullptr; } HandleLink onSymlink(const SymlinkInfo& si) override { return TraverserCallback::LINK_SKIP; } - HandleError reportDirError (const std::wstring& msg, size_t retryNumber ) override { setError(msg); return ON_ERROR_IGNORE; } - HandleError reportItemError(const std::wstring& msg, size_t retryNumber, const Zstring& itemName) override { setError(msg); return ON_ERROR_IGNORE; } + HandleError reportDirError (const std::wstring& msg, size_t retryNumber ) override { setError(msg); return ON_ERROR_CONTINUE; } + HandleError reportItemError(const std::wstring& msg, size_t retryNumber, const Zstring& itemName) override { setError(msg); return ON_ERROR_CONTINUE; } const std::vector<Zstring>& refFileNames() const { return logFileNames_; } const Opt<FileError>& getLastError() const { return lastError_; } @@ -118,7 +118,7 @@ void limitLogfileCount(const AbstractPath& logFolderPath, const std::wstring& jo { try { - AFS::removeFile(AFS::appendRelPath(logFolderPath, logFileName)); //throw FileError + AFS::removeFilePlain(AFS::appendRelPath(logFolderPath, logFileName)); //throw FileError } catch (const FileError& e) { if (!lastError) lastError = e; }; diff --git a/FreeFileSync/Source/ui/custom_grid.cpp b/FreeFileSync/Source/ui/custom_grid.cpp index b47e6de9..d1f0cf77 100644 --- a/FreeFileSync/Source/ui/custom_grid.cpp +++ b/FreeFileSync/Source/ui/custom_grid.cpp @@ -707,7 +707,7 @@ private: visitFSObject(*fsObj, [&](const FolderPair& folder) { out.type = IconInfo::FOLDER; - //todo: if ("is followed symlink") out.drawAsLink = true; + out.drawAsLink = folder.isFollowedSymlink<side>(); }, [&](const FilePair& file) diff --git a/FreeFileSync/Source/ui/folder_selector.cpp b/FreeFileSync/Source/ui/folder_selector.cpp index 1e14634d..f567de42 100644 --- a/FreeFileSync/Source/ui/folder_selector.cpp +++ b/FreeFileSync/Source/ui/folder_selector.cpp @@ -178,21 +178,13 @@ void FolderSelector::onFilesDropped(FileDropEvent& event) auto fmtShellPath = [](const Zstring& shellItemPath) { const AbstractPath itemPath = createAbstractPath(shellItemPath); - if (!AFS::folderExists(itemPath)) + try { - Zstring parentShellPath = beforeLast(shellItemPath, FILE_NAME_SEPARATOR, IF_MISSING_RETURN_NONE); - if (!parentShellPath.empty()) - { -#ifdef ZEN_WIN - if (endsWith(parentShellPath, L":")) //volume root - parentShellPath += FILE_NAME_SEPARATOR; -#endif - const AbstractPath parentPath = createAbstractPath(parentShellPath); - if (AFS::folderExists(parentPath)) - return AFS::getInitPathPhrase(parentPath); - //else: keep original name unconditionally: usecase: inactive mapped network shares - } + if (AFS::getItemType(itemPath) == AFS::ItemType::FILE) //throw FileError + if (Opt<AbstractPath> parentPath = AFS::getParentFolderPath(itemPath)) + return AFS::getInitPathPhrase(*parentPath); } + catch (FileError&) {} //e.g. good for inactive mapped network shares, not so nice for C:\pagefile.sys //make sure FFS-specific explicit MTP-syntax is applied! return AFS::getInitPathPhrase(itemPath); }; @@ -231,7 +223,14 @@ void FolderSelector::onSelectFolder(wxCommandEvent& event) { auto folderExistsTimed = [](const AbstractPath& folderPath) { - auto ft = runAsync([folderPath] { return AFS::folderExists(folderPath); }); + auto ft = runAsync([folderPath] + { + try + { + return AFS::getItemType(folderPath) != AFS::ItemType::FILE; //throw FileError + } + catch (FileError&) { return false; } + }); return ft.wait_for(std::chrono::milliseconds(200)) == std::future_status::ready && ft.get(); //potentially slow network access: wait 200ms at most }; diff --git a/FreeFileSync/Source/ui/gui_generated.cpp b/FreeFileSync/Source/ui/gui_generated.cpp index 2683f6a4..6db4dbad 100644 --- a/FreeFileSync/Source/ui/gui_generated.cpp +++ b/FreeFileSync/Source/ui/gui_generated.cpp @@ -19,7 +19,7 @@ MainDialogGenerated::MainDialogGenerated( wxWindow* parent, wxWindowID id, const wxString& title, const wxPoint& pos, const wxSize& size, long style ) : wxFrame( parent, id, title, pos, size, style ) { - this->SetSizeHints( wxSize( 640,400 ), wxDefaultSize ); + this->SetSizeHints( wxSize( 640, 400 ), wxDefaultSize ); m_menubar1 = new wxMenuBar( 0 ); m_menuFile = new wxMenu(); @@ -34,16 +34,16 @@ MainDialogGenerated::MainDialogGenerated( wxWindow* parent, wxWindowID id, const m_menuItemSave = new wxMenuItem( m_menuFile, wxID_SAVE, wxString( _("&Save") ) + wxT('\t') + wxT("Ctrl+S"), wxEmptyString, wxITEM_NORMAL ); m_menuFile->Append( m_menuItemSave ); - m_menuItemSaveAs = new wxMenuItem( m_menuFile, wxID_SAVEAS, wxString( _("Save &as...") ) , wxEmptyString, wxITEM_NORMAL ); + m_menuItemSaveAs = new wxMenuItem( m_menuFile, wxID_SAVEAS, wxString( _("Save &as...") ), wxEmptyString, wxITEM_NORMAL ); m_menuFile->Append( m_menuItemSaveAs ); - m_menuItemSaveAsBatch = new wxMenuItem( m_menuFile, wxID_ANY, wxString( _("Save as &batch job...") ) , wxEmptyString, wxITEM_NORMAL ); + m_menuItemSaveAsBatch = new wxMenuItem( m_menuFile, wxID_ANY, wxString( _("Save as &batch job...") ), wxEmptyString, wxITEM_NORMAL ); m_menuFile->Append( m_menuItemSaveAsBatch ); m_menuFile->AppendSeparator(); wxMenuItem* m_menuItem4; - m_menuItem4 = new wxMenuItem( m_menuFile, wxID_EXIT, wxString( _("E&xit") ) , wxEmptyString, wxITEM_NORMAL ); + m_menuItem4 = new wxMenuItem( m_menuFile, wxID_EXIT, wxString( _("E&xit") ), wxEmptyString, wxITEM_NORMAL ); m_menuFile->Append( m_menuItem4 ); m_menubar1->Append( m_menuFile, _("&File") ); @@ -84,11 +84,11 @@ MainDialogGenerated::MainDialogGenerated( wxWindow* parent, wxWindowID id, const m_menuTools->Append( m_menuItemFind ); wxMenuItem* m_menuItem51; - m_menuItem51 = new wxMenuItem( m_menuTools, wxID_ANY, wxString( _("&Reset layout") ) , wxEmptyString, wxITEM_NORMAL ); + m_menuItem51 = new wxMenuItem( m_menuTools, wxID_ANY, wxString( _("&Reset layout") ), wxEmptyString, wxITEM_NORMAL ); m_menuTools->Append( m_menuItem51 ); wxMenuItem* m_menuItem5; - m_menuItem5 = new wxMenuItem( m_menuTools, wxID_ANY, wxString( _("&Export file list...") ) , wxEmptyString, wxITEM_NORMAL ); + m_menuItem5 = new wxMenuItem( m_menuTools, wxID_ANY, wxString( _("&Export file list...") ), wxEmptyString, wxITEM_NORMAL ); m_menuTools->Append( m_menuItem5 ); m_menubar1->Append( m_menuTools, _("&Tools") ); @@ -99,10 +99,10 @@ MainDialogGenerated::MainDialogGenerated( wxWindow* parent, wxWindowID id, const m_menuHelp->AppendSeparator(); - m_menuItemCheckVersionNow = new wxMenuItem( m_menuHelp, wxID_ANY, wxString( _("&Check for updates now") ) , wxEmptyString, wxITEM_NORMAL ); + m_menuItemCheckVersionNow = new wxMenuItem( m_menuHelp, wxID_ANY, wxString( _("&Check for updates now") ), wxEmptyString, wxITEM_NORMAL ); m_menuHelp->Append( m_menuItemCheckVersionNow ); - m_menuItemCheckVersionAuto = new wxMenuItem( m_menuHelp, wxID_ANY, wxString( _("Check &automatically once a week") ) , wxEmptyString, wxITEM_CHECK ); + m_menuItemCheckVersionAuto = new wxMenuItem( m_menuHelp, wxID_ANY, wxString( _("Check &automatically once a week") ), wxEmptyString, wxITEM_CHECK ); m_menuHelp->Append( m_menuItemCheckVersionAuto ); m_menuItemCheckVersionAuto->Check( true ); @@ -126,14 +126,14 @@ MainDialogGenerated::MainDialogGenerated( wxWindow* parent, wxWindowID id, const 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 = 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 ) ); m_buttonCancel->Enable( false ); m_buttonCancel->Hide(); bSizerTopButtons->Add( m_buttonCancel, 0, wxEXPAND, 5 ); - m_buttonCompare = new zen::BitmapTextButton( m_panelTopButtons, wxID_ANY, _("Compare"), wxDefaultPosition, wxSize( -1,-1 ), 0 ); + m_buttonCompare = new zen::BitmapTextButton( m_panelTopButtons, wxID_ANY, _("Compare"), wxDefaultPosition, wxSize( -1, -1 ), 0 ); m_buttonCompare->SetDefault(); m_buttonCompare->SetFont( wxFont( wxNORMAL_FONT->GetPointSize(), 70, 90, 92, false, wxEmptyString ) ); m_buttonCompare->SetToolTip( _("dummy") ); @@ -146,12 +146,12 @@ MainDialogGenerated::MainDialogGenerated( wxWindow* parent, wxWindowID id, const wxBoxSizer* bSizer198; bSizer198 = new wxBoxSizer( wxHORIZONTAL ); - m_bpButtonCmpConfig = new wxBitmapButton( m_panelTopButtons, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( -1,-1 ), wxBU_AUTODRAW ); + m_bpButtonCmpConfig = new wxBitmapButton( m_panelTopButtons, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( -1, -1 ), wxBU_AUTODRAW ); m_bpButtonCmpConfig->SetToolTip( _("dummy") ); bSizer198->Add( m_bpButtonCmpConfig, 1, wxEXPAND, 5 ); - m_bpButtonCmpContext = new wxBitmapButton( m_panelTopButtons, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 17,-1 ), wxBU_AUTODRAW ); + m_bpButtonCmpContext = new wxBitmapButton( m_panelTopButtons, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 17, -1 ), wxBU_AUTODRAW ); m_bpButtonCmpContext->SetToolTip( _("dummy") ); bSizer198->Add( m_bpButtonCmpContext, 0, wxEXPAND, 5 ); @@ -168,10 +168,10 @@ MainDialogGenerated::MainDialogGenerated( wxWindow* parent, wxWindowID id, const wxBoxSizer* bSizer199; bSizer199 = new wxBoxSizer( wxHORIZONTAL ); - m_bpButtonFilter = new wxBitmapButton( m_panelTopButtons, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 60,-1 ), wxBU_AUTODRAW|wxFULL_REPAINT_ON_RESIZE ); + m_bpButtonFilter = new wxBitmapButton( m_panelTopButtons, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 60, -1 ), wxBU_AUTODRAW|wxFULL_REPAINT_ON_RESIZE ); bSizer199->Add( m_bpButtonFilter, 1, wxEXPAND, 5 ); - m_bpButtonFilterContext = new wxBitmapButton( m_panelTopButtons, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 17,-1 ), wxBU_AUTODRAW ); + m_bpButtonFilterContext = new wxBitmapButton( m_panelTopButtons, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 17, -1 ), wxBU_AUTODRAW ); m_bpButtonFilterContext->SetToolTip( _("dummy") ); bSizer199->Add( m_bpButtonFilterContext, 0, wxEXPAND, 5 ); @@ -188,12 +188,12 @@ MainDialogGenerated::MainDialogGenerated( wxWindow* parent, wxWindowID id, const wxBoxSizer* bSizer200; bSizer200 = new wxBoxSizer( wxHORIZONTAL ); - m_bpButtonSyncConfig = new wxBitmapButton( m_panelTopButtons, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( -1,-1 ), wxBU_AUTODRAW ); + m_bpButtonSyncConfig = new wxBitmapButton( m_panelTopButtons, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( -1, -1 ), wxBU_AUTODRAW ); m_bpButtonSyncConfig->SetToolTip( _("dummy") ); bSizer200->Add( m_bpButtonSyncConfig, 1, wxEXPAND, 5 ); - m_bpButtonSyncContext = new wxBitmapButton( m_panelTopButtons, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 17,-1 ), wxBU_AUTODRAW ); + m_bpButtonSyncContext = new wxBitmapButton( m_panelTopButtons, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 17, -1 ), wxBU_AUTODRAW ); m_bpButtonSyncContext->SetToolTip( _("dummy") ); bSizer200->Add( m_bpButtonSyncContext, 0, wxEXPAND, 5 ); @@ -204,7 +204,7 @@ MainDialogGenerated::MainDialogGenerated( wxWindow* parent, wxWindowID id, const bSizerTopButtons->Add( 4, 0, 0, 0, 5 ); - m_buttonSync = new zen::BitmapTextButton( m_panelTopButtons, wxID_ANY, _("Synchronize"), wxDefaultPosition, wxSize( -1,-1 ), 0 ); + m_buttonSync = new zen::BitmapTextButton( m_panelTopButtons, wxID_ANY, _("Synchronize"), wxDefaultPosition, wxSize( -1, -1 ), 0 ); m_buttonSync->SetFont( wxFont( wxNORMAL_FONT->GetPointSize(), 70, 90, 92, false, wxEmptyString ) ); m_buttonSync->SetToolTip( _("dummy") ); @@ -230,7 +230,7 @@ MainDialogGenerated::MainDialogGenerated( wxWindow* parent, wxWindowID id, const bSizer91 = new wxBoxSizer( wxHORIZONTAL ); m_panelTopLeft = new wxPanel( m_panelDirectoryPairs, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL ); - m_panelTopLeft->SetMinSize( wxSize( 1,-1 ) ); + m_panelTopLeft->SetMinSize( wxSize( 1, -1 ) ); wxFlexGridSizer* fgSizer8; fgSizer8 = new wxFlexGridSizer( 0, 2, 0, 0 ); @@ -248,12 +248,12 @@ MainDialogGenerated::MainDialogGenerated( wxWindow* parent, wxWindowID id, const wxBoxSizer* bSizer159; bSizer159 = new wxBoxSizer( wxHORIZONTAL ); - m_bpButtonAddPair = new wxBitmapButton( m_panelTopLeft, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 25,25 ), wxBU_AUTODRAW ); + m_bpButtonAddPair = new wxBitmapButton( m_panelTopLeft, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 25, 25 ), wxBU_AUTODRAW ); m_bpButtonAddPair->SetToolTip( _("Add folder pair") ); bSizer159->Add( m_bpButtonAddPair, 0, wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 5 ); - m_bpButtonRemovePair = new wxBitmapButton( m_panelTopLeft, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 25,25 ), wxBU_AUTODRAW ); + m_bpButtonRemovePair = new wxBitmapButton( m_panelTopLeft, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 25, 25 ), wxBU_AUTODRAW ); m_bpButtonRemovePair->SetToolTip( _("Remove folder pair") ); bSizer159->Add( m_bpButtonRemovePair, 0, wxALIGN_CENTER_VERTICAL, 5 ); @@ -272,7 +272,7 @@ MainDialogGenerated::MainDialogGenerated( wxWindow* parent, wxWindowID id, const bSizer182->Add( m_buttonSelectFolderLeft, 0, wxALIGN_CENTER_VERTICAL, 5 ); - m_bpButtonSelectAltFolderLeft = new wxBitmapButton( m_panelTopLeft, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 26,-1 ), wxBU_AUTODRAW ); + m_bpButtonSelectAltFolderLeft = new wxBitmapButton( m_panelTopLeft, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 26, -1 ), wxBU_AUTODRAW ); m_bpButtonSelectAltFolderLeft->SetToolTip( _("Select SFTP folder") ); bSizer182->Add( m_bpButtonSelectAltFolderLeft, 0, wxEXPAND, 5 ); @@ -293,7 +293,7 @@ MainDialogGenerated::MainDialogGenerated( wxWindow* parent, wxWindowID id, const bSizer1771->Add( 0, 0, 1, wxEXPAND, 5 ); - m_bpButtonSwapSides = new wxBitmapButton( m_panelTopCenter, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( -1,-1 ), wxBU_AUTODRAW ); + m_bpButtonSwapSides = new wxBitmapButton( m_panelTopCenter, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( -1, -1 ), wxBU_AUTODRAW ); m_bpButtonSwapSides->SetToolTip( _("Swap sides") ); bSizer1771->Add( m_bpButtonSwapSides, 0, wxEXPAND, 5 ); @@ -301,13 +301,13 @@ MainDialogGenerated::MainDialogGenerated( wxWindow* parent, wxWindowID id, const wxBoxSizer* bSizer160; bSizer160 = new wxBoxSizer( wxHORIZONTAL ); - m_bpButtonAltCompCfg = new wxBitmapButton( m_panelTopCenter, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 26,25 ), wxBU_AUTODRAW ); + m_bpButtonAltCompCfg = new wxBitmapButton( m_panelTopCenter, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 26, 25 ), wxBU_AUTODRAW ); bSizer160->Add( m_bpButtonAltCompCfg, 0, wxALIGN_CENTER_VERTICAL, 5 ); - m_bpButtonLocalFilter = new wxBitmapButton( m_panelTopCenter, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 26,25 ), wxBU_AUTODRAW ); + m_bpButtonLocalFilter = new wxBitmapButton( m_panelTopCenter, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 26, 25 ), wxBU_AUTODRAW ); bSizer160->Add( m_bpButtonLocalFilter, 0, wxALIGN_CENTER_VERTICAL, 5 ); - m_bpButtonAltSyncCfg = new wxBitmapButton( m_panelTopCenter, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 26,25 ), wxBU_AUTODRAW ); + m_bpButtonAltSyncCfg = new wxBitmapButton( m_panelTopCenter, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 26, 25 ), wxBU_AUTODRAW ); bSizer160->Add( m_bpButtonAltSyncCfg, 0, wxALIGN_CENTER_VERTICAL, 5 ); @@ -323,7 +323,7 @@ MainDialogGenerated::MainDialogGenerated( wxWindow* parent, wxWindowID id, const bSizer91->Add( m_panelTopCenter, 0, wxRIGHT|wxLEFT|wxEXPAND, 5 ); m_panelTopRight = new wxPanel( m_panelDirectoryPairs, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL ); - m_panelTopRight->SetMinSize( wxSize( 1,-1 ) ); + m_panelTopRight->SetMinSize( wxSize( 1, -1 ) ); wxBoxSizer* bSizer183; bSizer183 = new wxBoxSizer( wxVERTICAL ); @@ -343,7 +343,7 @@ MainDialogGenerated::MainDialogGenerated( wxWindow* parent, wxWindowID id, const bSizer179->Add( m_buttonSelectFolderRight, 0, wxALIGN_CENTER_VERTICAL, 5 ); - m_bpButtonSelectAltFolderRight = new wxBitmapButton( m_panelTopRight, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 26,-1 ), wxBU_AUTODRAW ); + m_bpButtonSelectAltFolderRight = new wxBitmapButton( m_panelTopRight, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 26, -1 ), wxBU_AUTODRAW ); m_bpButtonSelectAltFolderRight->SetToolTip( _("Select SFTP folder") ); bSizer179->Add( m_bpButtonSelectAltFolderRight, 0, wxEXPAND, 5 ); @@ -360,9 +360,9 @@ MainDialogGenerated::MainDialogGenerated( wxWindow* parent, wxWindowID id, const bSizer1601->Add( bSizer91, 0, wxEXPAND, 5 ); - m_scrolledWindowFolderPairs = new wxScrolledWindow( m_panelDirectoryPairs, wxID_ANY, wxDefaultPosition, wxSize( -1,-1 ), wxHSCROLL|wxVSCROLL ); + m_scrolledWindowFolderPairs = new wxScrolledWindow( m_panelDirectoryPairs, wxID_ANY, wxDefaultPosition, wxSize( -1, -1 ), wxHSCROLL|wxVSCROLL ); m_scrolledWindowFolderPairs->SetScrollRate( 10, 10 ); - m_scrolledWindowFolderPairs->SetMinSize( wxSize( -1,0 ) ); + m_scrolledWindowFolderPairs->SetMinSize( wxSize( -1, 0 ) ); bSizerAddFolderPairs = new wxBoxSizer( wxVERTICAL ); @@ -412,7 +412,7 @@ MainDialogGenerated::MainDialogGenerated( wxWindow* parent, wxWindowID id, const wxBoxSizer* bSizer451; bSizer451 = new wxBoxSizer( wxHORIZONTAL ); - bSizer451->SetMinSize( wxSize( -1,22 ) ); + bSizer451->SetMinSize( wxSize( -1, 22 ) ); bSizerFileStatus = new wxBoxSizer( wxHORIZONTAL ); bSizerStatusLeft = new wxBoxSizer( wxHORIZONTAL ); @@ -570,7 +570,7 @@ MainDialogGenerated::MainDialogGenerated( wxWindow* parent, wxWindowID id, const wxBoxSizer* bSizer1713; bSizer1713 = new wxBoxSizer( wxHORIZONTAL ); - m_bpButtonHideSearch = new wxBitmapButton( m_panelSearch, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 25,25 ), wxBU_AUTODRAW ); + m_bpButtonHideSearch = new wxBitmapButton( m_panelSearch, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 25, 25 ), wxBU_AUTODRAW ); m_bpButtonHideSearch->SetToolTip( _("Close search bar") ); bSizer1713->Add( m_bpButtonHideSearch, 0, wxALIGN_CENTER_VERTICAL|wxRIGHT, 5 ); @@ -579,7 +579,7 @@ MainDialogGenerated::MainDialogGenerated( wxWindow* parent, wxWindowID id, const m_staticText101->Wrap( -1 ); bSizer1713->Add( m_staticText101, 0, wxALIGN_CENTER_VERTICAL, 5 ); - m_textCtrlSearchTxt = new wxTextCtrl( m_panelSearch, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize( 220,-1 ), wxTE_PROCESS_ENTER|wxWANTS_CHARS ); + m_textCtrlSearchTxt = new wxTextCtrl( m_panelSearch, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize( 220, -1 ), wxTE_PROCESS_ENTER|wxWANTS_CHARS ); m_textCtrlSearchTxt->SetMaxLength( 0 ); bSizer1713->Add( m_textCtrlSearchTxt, 0, wxALIGN_CENTER_VERTICAL|wxRIGHT|wxLEFT, 5 ); @@ -601,7 +601,7 @@ MainDialogGenerated::MainDialogGenerated( wxWindow* parent, wxWindowID id, const wxBoxSizer* bSizer17611; bSizer17611 = new wxBoxSizer( wxVERTICAL ); - m_bpButtonNew = new wxBitmapButton( m_panelConfig, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 42,42 ), wxBU_AUTODRAW ); + m_bpButtonNew = new wxBitmapButton( m_panelConfig, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 42, 42 ), wxBU_AUTODRAW ); m_bpButtonNew->SetToolTip( _("dummy") ); bSizer17611->Add( m_bpButtonNew, 0, wxEXPAND, 5 ); @@ -616,7 +616,7 @@ MainDialogGenerated::MainDialogGenerated( wxWindow* parent, wxWindowID id, const wxBoxSizer* bSizer1761; bSizer1761 = new wxBoxSizer( wxVERTICAL ); - m_bpButtonOpen = new wxBitmapButton( m_panelConfig, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 42,42 ), wxBU_AUTODRAW ); + m_bpButtonOpen = new wxBitmapButton( m_panelConfig, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 42, 42 ), wxBU_AUTODRAW ); m_bpButtonOpen->SetToolTip( _("dummy") ); bSizer1761->Add( m_bpButtonOpen, 0, wxEXPAND, 5 ); @@ -631,7 +631,7 @@ MainDialogGenerated::MainDialogGenerated( wxWindow* parent, wxWindowID id, const wxBoxSizer* bSizer175; bSizer175 = new wxBoxSizer( wxVERTICAL ); - m_bpButtonSave = new wxBitmapButton( m_panelConfig, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 42,42 ), wxBU_AUTODRAW ); + m_bpButtonSave = new wxBitmapButton( m_panelConfig, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 42, 42 ), wxBU_AUTODRAW ); m_bpButtonSave->SetToolTip( _("dummy") ); bSizer175->Add( m_bpButtonSave, 0, wxEXPAND, 5 ); @@ -649,12 +649,12 @@ MainDialogGenerated::MainDialogGenerated( wxWindow* parent, wxWindowID id, const wxBoxSizer* bSizer1772; bSizer1772 = new wxBoxSizer( wxHORIZONTAL ); - m_bpButtonSaveAs = new wxBitmapButton( m_panelConfig, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 42,42 ), wxBU_AUTODRAW ); + m_bpButtonSaveAs = new wxBitmapButton( m_panelConfig, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 42, 42 ), wxBU_AUTODRAW ); m_bpButtonSaveAs->SetToolTip( _("dummy") ); bSizer1772->Add( m_bpButtonSaveAs, 1, 0, 5 ); - m_bpButtonSaveAsBatch = new wxBitmapButton( m_panelConfig, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 42,42 ), wxBU_AUTODRAW ); + m_bpButtonSaveAsBatch = new wxBitmapButton( m_panelConfig, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 42, 42 ), wxBU_AUTODRAW ); m_bpButtonSaveAsBatch->SetToolTip( _("dummy") ); bSizer1772->Add( m_bpButtonSaveAsBatch, 1, 0, 5 ); @@ -673,7 +673,7 @@ MainDialogGenerated::MainDialogGenerated( wxWindow* parent, wxWindowID id, const bSizerConfig->Add( bSizer151, 0, wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 5 ); m_listBoxHistory = new wxListBox( m_panelConfig, wxID_ANY, wxDefaultPosition, wxDefaultSize, 0, NULL, wxLB_EXTENDED|wxLB_NEEDED_SB ); - m_listBoxHistory->SetMinSize( wxSize( -1,40 ) ); + m_listBoxHistory->SetMinSize( wxSize( -1, 40 ) ); bSizerConfig->Add( m_listBoxHistory, 1, wxEXPAND, 5 ); @@ -690,10 +690,10 @@ MainDialogGenerated::MainDialogGenerated( wxWindow* parent, wxWindowID id, const m_staticTextViewType->Wrap( -1 ); bSizerViewFilter->Add( m_staticTextViewType, 0, wxALL|wxALIGN_CENTER_VERTICAL|wxALIGN_CENTER_HORIZONTAL, 5 ); - m_bpButtonViewTypeSyncAction = new ToggleButton( m_panelViewFilter, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 82,42 ), wxBU_AUTODRAW ); + m_bpButtonViewTypeSyncAction = new ToggleButton( m_panelViewFilter, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 82, 42 ), wxBU_AUTODRAW ); bSizerViewFilter->Add( m_bpButtonViewTypeSyncAction, 0, wxALIGN_CENTER_VERTICAL|wxALIGN_CENTER_HORIZONTAL|wxRIGHT, 5 ); - m_bpButtonShowExcluded = new ToggleButton( m_panelViewFilter, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 42,42 ), wxBU_AUTODRAW ); + m_bpButtonShowExcluded = new ToggleButton( m_panelViewFilter, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 42, 42 ), wxBU_AUTODRAW ); bSizerViewFilter->Add( m_bpButtonShowExcluded, 0, wxALIGN_CENTER_VERTICAL|wxALIGN_CENTER_HORIZONTAL, 5 ); @@ -703,46 +703,46 @@ MainDialogGenerated::MainDialogGenerated( wxWindow* parent, wxWindowID id, const m_staticTextSelectView->Wrap( -1 ); bSizerViewFilter->Add( m_staticTextSelectView, 0, wxALL|wxALIGN_CENTER_VERTICAL|wxALIGN_CENTER_HORIZONTAL, 5 ); - m_bpButtonShowDeleteLeft = new ToggleButton( m_panelViewFilter, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 42,42 ), wxBU_AUTODRAW ); + m_bpButtonShowDeleteLeft = new ToggleButton( m_panelViewFilter, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 42, 42 ), wxBU_AUTODRAW ); bSizerViewFilter->Add( m_bpButtonShowDeleteLeft, 0, wxALIGN_CENTER_VERTICAL|wxALIGN_CENTER_HORIZONTAL, 5 ); - m_bpButtonShowUpdateLeft = new ToggleButton( m_panelViewFilter, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 42,42 ), wxBU_AUTODRAW ); + m_bpButtonShowUpdateLeft = new ToggleButton( m_panelViewFilter, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 42, 42 ), wxBU_AUTODRAW ); bSizerViewFilter->Add( m_bpButtonShowUpdateLeft, 0, wxALIGN_CENTER_VERTICAL|wxALIGN_CENTER_HORIZONTAL, 5 ); - m_bpButtonShowCreateLeft = new ToggleButton( m_panelViewFilter, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 42,42 ), wxBU_AUTODRAW ); + m_bpButtonShowCreateLeft = new ToggleButton( m_panelViewFilter, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 42, 42 ), wxBU_AUTODRAW ); bSizerViewFilter->Add( m_bpButtonShowCreateLeft, 0, wxALIGN_CENTER_VERTICAL|wxALIGN_CENTER_HORIZONTAL, 5 ); - m_bpButtonShowLeftOnly = new ToggleButton( m_panelViewFilter, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 42,42 ), wxBU_AUTODRAW ); + m_bpButtonShowLeftOnly = new ToggleButton( m_panelViewFilter, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 42, 42 ), wxBU_AUTODRAW ); bSizerViewFilter->Add( m_bpButtonShowLeftOnly, 0, wxALIGN_CENTER_VERTICAL|wxALIGN_CENTER_HORIZONTAL, 5 ); - m_bpButtonShowLeftNewer = new ToggleButton( m_panelViewFilter, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 42,42 ), wxBU_AUTODRAW ); + m_bpButtonShowLeftNewer = new ToggleButton( m_panelViewFilter, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 42, 42 ), wxBU_AUTODRAW ); bSizerViewFilter->Add( m_bpButtonShowLeftNewer, 0, wxALIGN_CENTER_VERTICAL|wxALIGN_CENTER_HORIZONTAL, 5 ); - m_bpButtonShowEqual = new ToggleButton( m_panelViewFilter, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 42,42 ), wxBU_AUTODRAW ); + m_bpButtonShowEqual = new ToggleButton( m_panelViewFilter, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 42, 42 ), wxBU_AUTODRAW ); bSizerViewFilter->Add( m_bpButtonShowEqual, 0, wxALIGN_CENTER_VERTICAL|wxALIGN_CENTER_HORIZONTAL, 5 ); - m_bpButtonShowDoNothing = new ToggleButton( m_panelViewFilter, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 42,42 ), wxBU_AUTODRAW ); + m_bpButtonShowDoNothing = new ToggleButton( m_panelViewFilter, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 42, 42 ), wxBU_AUTODRAW ); bSizerViewFilter->Add( m_bpButtonShowDoNothing, 0, wxALIGN_CENTER_VERTICAL|wxALIGN_CENTER_HORIZONTAL, 5 ); - m_bpButtonShowDifferent = new ToggleButton( m_panelViewFilter, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 42,42 ), wxBU_AUTODRAW ); + m_bpButtonShowDifferent = new ToggleButton( m_panelViewFilter, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 42, 42 ), wxBU_AUTODRAW ); bSizerViewFilter->Add( m_bpButtonShowDifferent, 0, wxALIGN_CENTER_VERTICAL|wxALIGN_CENTER_HORIZONTAL, 5 ); - m_bpButtonShowRightNewer = new ToggleButton( m_panelViewFilter, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 42,42 ), wxBU_AUTODRAW ); + m_bpButtonShowRightNewer = new ToggleButton( m_panelViewFilter, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 42, 42 ), wxBU_AUTODRAW ); bSizerViewFilter->Add( m_bpButtonShowRightNewer, 0, wxALIGN_CENTER_VERTICAL|wxALIGN_CENTER_HORIZONTAL, 5 ); - m_bpButtonShowRightOnly = new ToggleButton( m_panelViewFilter, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 42,42 ), wxBU_AUTODRAW ); + m_bpButtonShowRightOnly = new ToggleButton( m_panelViewFilter, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 42, 42 ), wxBU_AUTODRAW ); bSizerViewFilter->Add( m_bpButtonShowRightOnly, 0, wxALIGN_CENTER_VERTICAL|wxALIGN_CENTER_HORIZONTAL, 5 ); - m_bpButtonShowCreateRight = new ToggleButton( m_panelViewFilter, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 42,42 ), wxBU_AUTODRAW ); + m_bpButtonShowCreateRight = new ToggleButton( m_panelViewFilter, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 42, 42 ), wxBU_AUTODRAW ); bSizerViewFilter->Add( m_bpButtonShowCreateRight, 0, wxALIGN_CENTER_VERTICAL|wxALIGN_CENTER_HORIZONTAL, 5 ); - m_bpButtonShowUpdateRight = new ToggleButton( m_panelViewFilter, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 42,42 ), wxBU_AUTODRAW ); + m_bpButtonShowUpdateRight = new ToggleButton( m_panelViewFilter, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 42, 42 ), wxBU_AUTODRAW ); bSizerViewFilter->Add( m_bpButtonShowUpdateRight, 0, wxALIGN_CENTER_VERTICAL|wxALIGN_CENTER_HORIZONTAL, 5 ); - m_bpButtonShowDeleteRight = new ToggleButton( m_panelViewFilter, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 42,42 ), wxBU_AUTODRAW ); + m_bpButtonShowDeleteRight = new ToggleButton( m_panelViewFilter, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 42, 42 ), wxBU_AUTODRAW ); bSizerViewFilter->Add( m_bpButtonShowDeleteRight, 0, wxALIGN_CENTER_VERTICAL|wxALIGN_CENTER_HORIZONTAL, 5 ); - m_bpButtonShowConflict = new ToggleButton( m_panelViewFilter, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 42,42 ), wxBU_AUTODRAW ); + m_bpButtonShowConflict = new ToggleButton( m_panelViewFilter, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 42, 42 ), wxBU_AUTODRAW ); bSizerViewFilter->Add( m_bpButtonShowConflict, 0, wxALIGN_CENTER_VERTICAL|wxALIGN_CENTER_HORIZONTAL, 5 ); @@ -1117,7 +1117,7 @@ ConfigDlgGenerated::ConfigDlgGenerated( wxWindow* parent, wxWindowID id, const w m_bitmapByTimeSize = new wxStaticBitmap( m_panelComparisonSettings, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxDefaultSize, 0 ); fgSizer16->Add( m_bitmapByTimeSize, 0, wxALIGN_CENTER_VERTICAL|wxALIGN_CENTER_HORIZONTAL, 5 ); - m_toggleBtnByTimeSize = new wxToggleButton( m_panelComparisonSettings, wxID_ANY, _("File time and size"), wxDefaultPosition, wxSize( -1,-1 ), 0 ); + m_toggleBtnByTimeSize = new wxToggleButton( m_panelComparisonSettings, wxID_ANY, _("File time and size"), wxDefaultPosition, wxSize( -1, -1 ), 0 ); m_toggleBtnByTimeSize->SetFont( wxFont( wxNORMAL_FONT->GetPointSize(), 70, 90, 92, false, wxEmptyString ) ); fgSizer16->Add( m_toggleBtnByTimeSize, 0, wxEXPAND, 5 ); @@ -1125,7 +1125,7 @@ ConfigDlgGenerated::ConfigDlgGenerated( wxWindow* parent, wxWindowID id, const w m_bitmapByContent = new wxStaticBitmap( m_panelComparisonSettings, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxDefaultSize, 0 ); fgSizer16->Add( m_bitmapByContent, 0, wxALIGN_CENTER_VERTICAL|wxALIGN_CENTER_HORIZONTAL, 5 ); - m_toggleBtnByContent = new wxToggleButton( m_panelComparisonSettings, wxID_ANY, _("File content"), wxDefaultPosition, wxSize( -1,-1 ), 0 ); + m_toggleBtnByContent = new wxToggleButton( m_panelComparisonSettings, wxID_ANY, _("File content"), wxDefaultPosition, wxSize( -1, -1 ), 0 ); m_toggleBtnByContent->SetFont( wxFont( wxNORMAL_FONT->GetPointSize(), 70, 90, 92, false, wxEmptyString ) ); fgSizer16->Add( m_toggleBtnByContent, 0, wxEXPAND, 5 ); @@ -1133,7 +1133,7 @@ ConfigDlgGenerated::ConfigDlgGenerated( wxWindow* parent, wxWindowID id, const w m_bitmapBySize = new wxStaticBitmap( m_panelComparisonSettings, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxDefaultSize, 0 ); fgSizer16->Add( m_bitmapBySize, 0, wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 5 ); - m_toggleBtnBySize = new wxToggleButton( m_panelComparisonSettings, wxID_ANY, _("File size"), wxDefaultPosition, wxSize( -1,-1 ), 0 ); + m_toggleBtnBySize = new wxToggleButton( m_panelComparisonSettings, wxID_ANY, _("File size"), wxDefaultPosition, wxSize( -1, -1 ), 0 ); m_toggleBtnBySize->SetValue( true ); m_toggleBtnBySize->SetFont( wxFont( wxNORMAL_FONT->GetPointSize(), 70, 90, 92, false, wxEmptyString ) ); @@ -1281,7 +1281,7 @@ ConfigDlgGenerated::ConfigDlgGenerated( wxWindow* parent, wxWindowID id, const w wxBoxSizer* bSizer1661; bSizer1661 = new wxBoxSizer( wxHORIZONTAL ); - m_bitmapInclude = new wxStaticBitmap( m_panelFilterSettings, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 30,30 ), 0 ); + m_bitmapInclude = new wxStaticBitmap( m_panelFilterSettings, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 30, 30 ), 0 ); bSizer1661->Add( m_bitmapInclude, 0, wxALIGN_CENTER_VERTICAL|wxALIGN_CENTER_HORIZONTAL|wxALL, 5 ); wxBoxSizer* bSizer1731; @@ -1291,8 +1291,8 @@ ConfigDlgGenerated::ConfigDlgGenerated( wxWindow* parent, wxWindowID id, const w m_staticText78->Wrap( -1 ); bSizer1731->Add( m_staticText78, 0, 0, 5 ); - m_textCtrlInclude = new wxTextCtrl( m_panelFilterSettings, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize( -1,-1 ), wxTE_MULTILINE ); - m_textCtrlInclude->SetMinSize( wxSize( 280,-1 ) ); + m_textCtrlInclude = new wxTextCtrl( m_panelFilterSettings, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize( -1, -1 ), wxTE_MULTILINE ); + m_textCtrlInclude->SetMinSize( wxSize( 280, -1 ) ); bSizer1731->Add( m_textCtrlInclude, 1, wxEXPAND|wxTOP, 5 ); @@ -1311,7 +1311,7 @@ ConfigDlgGenerated::ConfigDlgGenerated( wxWindow* parent, wxWindowID id, const w wxBoxSizer* bSizer1651; bSizer1651 = new wxBoxSizer( wxHORIZONTAL ); - m_bitmapExclude = new wxStaticBitmap( m_panelFilterSettings, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 30,30 ), 0 ); + m_bitmapExclude = new wxStaticBitmap( m_panelFilterSettings, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 30, 30 ), 0 ); bSizer1651->Add( m_bitmapExclude, 0, wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL|wxALL, 5 ); wxBoxSizer* bSizer1742; @@ -1333,7 +1333,7 @@ ConfigDlgGenerated::ConfigDlgGenerated( wxWindow* parent, wxWindowID id, const w bSizer1742->Add( bSizer189, 0, wxEXPAND, 5 ); - m_textCtrlExclude = new wxTextCtrl( m_panelFilterSettings, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize( -1,-1 ), wxTE_MULTILINE ); + m_textCtrlExclude = new wxTextCtrl( m_panelFilterSettings, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize( -1, -1 ), wxTE_MULTILINE ); bSizer1742->Add( m_textCtrlExclude, 1, wxEXPAND|wxTOP, 5 ); @@ -1354,7 +1354,7 @@ ConfigDlgGenerated::ConfigDlgGenerated( wxWindow* parent, wxWindowID id, const w wxBoxSizer* bSizer167; bSizer167 = new wxBoxSizer( wxHORIZONTAL ); - m_bitmapFilterDate = new wxStaticBitmap( m_panelFilterSettings, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 34,34 ), 0 ); + m_bitmapFilterDate = new wxStaticBitmap( m_panelFilterSettings, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 34, 34 ), 0 ); bSizer167->Add( m_bitmapFilterDate, 0, wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL|wxALL, 5 ); wxBoxSizer* bSizer165; @@ -1384,7 +1384,7 @@ ConfigDlgGenerated::ConfigDlgGenerated( wxWindow* parent, wxWindowID id, const w wxBoxSizer* bSizer168; bSizer168 = new wxBoxSizer( wxHORIZONTAL ); - m_bitmapFilterSize = new wxStaticBitmap( m_panelFilterSettings, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 32,32 ), 0 ); + m_bitmapFilterSize = new wxStaticBitmap( m_panelFilterSettings, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 32, 32 ), 0 ); bSizer168->Add( m_bitmapFilterSize, 0, wxALIGN_CENTER_VERTICAL|wxALIGN_CENTER_HORIZONTAL|wxALL, 5 ); wxBoxSizer* bSizer158; @@ -1451,7 +1451,7 @@ ConfigDlgGenerated::ConfigDlgGenerated( wxWindow* parent, wxWindowID id, const w wxBoxSizer* bSizer280; bSizer280 = new wxBoxSizer( wxHORIZONTAL ); - m_staticText44 = new wxStaticText( m_panelFilterSettingsHolder, wxID_ANY, _("Select filter rules to exclude certain files from synchronization. Enter file paths relative to their corresponding folder pair."), wxDefaultPosition, wxSize( -1,-1 ), 0 ); + m_staticText44 = new wxStaticText( m_panelFilterSettingsHolder, wxID_ANY, _("Select filter rules to exclude certain files from synchronization. Enter file paths relative to their corresponding folder pair."), wxDefaultPosition, wxSize( -1, -1 ), 0 ); m_staticText44->Wrap( 590 ); bSizer280->Add( m_staticText44, 0, wxALIGN_CENTER_VERTICAL|wxALL, 10 ); @@ -1461,7 +1461,7 @@ ConfigDlgGenerated::ConfigDlgGenerated( wxWindow* parent, wxWindowID id, const w m_staticline46 = new wxStaticLine( m_panelFilterSettingsHolder, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxLI_VERTICAL ); bSizer280->Add( m_staticline46, 0, wxEXPAND, 5 ); - m_buttonClear = new wxButton( m_panelFilterSettingsHolder, wxID_ANY, _("C&lear"), wxDefaultPosition, wxSize( -1,-1 ), 0 ); + m_buttonClear = new wxButton( m_panelFilterSettingsHolder, wxID_ANY, _("C&lear"), wxDefaultPosition, wxSize( -1, -1 ), 0 ); bSizer280->Add( m_buttonClear, 0, wxALL|wxALIGN_CENTER_VERTICAL, 10 ); @@ -1512,22 +1512,22 @@ ConfigDlgGenerated::ConfigDlgGenerated( wxWindow* parent, wxWindowID id, const w wxBoxSizer* bSizer236; bSizer236 = new wxBoxSizer( wxVERTICAL ); - m_toggleBtnTwoWay = new wxToggleButton( m_panelSyncSettings, wxID_ANY, _("dummy"), wxDefaultPosition, wxSize( -1,30 ), 0 ); + m_toggleBtnTwoWay = new wxToggleButton( m_panelSyncSettings, wxID_ANY, _("dummy"), wxDefaultPosition, wxSize( -1, 30 ), 0 ); m_toggleBtnTwoWay->SetFont( wxFont( wxNORMAL_FONT->GetPointSize(), 70, 90, 92, false, wxEmptyString ) ); bSizer236->Add( m_toggleBtnTwoWay, 0, wxEXPAND|wxBOTTOM, 5 ); - m_toggleBtnMirror = new wxToggleButton( m_panelSyncSettings, wxID_ANY, _("dummy"), wxDefaultPosition, wxSize( -1,30 ), 0 ); + m_toggleBtnMirror = new wxToggleButton( m_panelSyncSettings, wxID_ANY, _("dummy"), wxDefaultPosition, wxSize( -1, 30 ), 0 ); m_toggleBtnMirror->SetFont( wxFont( wxNORMAL_FONT->GetPointSize(), 70, 90, 92, false, wxEmptyString ) ); bSizer236->Add( m_toggleBtnMirror, 0, wxEXPAND|wxBOTTOM, 5 ); - m_toggleBtnUpdate = new wxToggleButton( m_panelSyncSettings, wxID_ANY, _("dummy"), wxDefaultPosition, wxSize( -1,30 ), 0 ); + m_toggleBtnUpdate = new wxToggleButton( m_panelSyncSettings, wxID_ANY, _("dummy"), wxDefaultPosition, wxSize( -1, 30 ), 0 ); m_toggleBtnUpdate->SetFont( wxFont( wxNORMAL_FONT->GetPointSize(), 70, 90, 92, false, wxEmptyString ) ); bSizer236->Add( m_toggleBtnUpdate, 0, wxEXPAND|wxBOTTOM, 5 ); - m_toggleBtnCustom = new wxToggleButton( m_panelSyncSettings, wxID_ANY, _("dummy"), wxDefaultPosition, wxSize( -1,30 ), 0 ); + m_toggleBtnCustom = new wxToggleButton( m_panelSyncSettings, wxID_ANY, _("dummy"), wxDefaultPosition, wxSize( -1, 30 ), 0 ); m_toggleBtnCustom->SetFont( wxFont( wxNORMAL_FONT->GetPointSize(), 70, 90, 92, false, wxEmptyString ) ); bSizer236->Add( m_toggleBtnCustom, 0, wxEXPAND, 5 ); @@ -1579,58 +1579,58 @@ ConfigDlgGenerated::ConfigDlgGenerated( wxWindow* parent, wxWindowID id, const w fgSizerSyncDirections->SetFlexibleDirection( wxBOTH ); fgSizerSyncDirections->SetNonFlexibleGrowMode( wxFLEX_GROWMODE_SPECIFIED ); - m_bitmapLeftOnly = new wxStaticBitmap( m_panelSyncSettings, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 45,45 ), 0 ); + m_bitmapLeftOnly = new wxStaticBitmap( m_panelSyncSettings, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 45, 45 ), 0 ); m_bitmapLeftOnly->SetToolTip( _("Item exists on left side only") ); fgSizerSyncDirections->Add( m_bitmapLeftOnly, 0, wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 5 ); - m_bitmapLeftNewer = new wxStaticBitmap( m_panelSyncSettings, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 45,45 ), 0 ); + m_bitmapLeftNewer = new wxStaticBitmap( m_panelSyncSettings, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 45, 45 ), 0 ); m_bitmapLeftNewer->SetToolTip( _("Left side is newer") ); fgSizerSyncDirections->Add( m_bitmapLeftNewer, 0, wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 5 ); - m_bitmapDifferent = new wxStaticBitmap( m_panelSyncSettings, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 45,45 ), 0 ); + m_bitmapDifferent = new wxStaticBitmap( m_panelSyncSettings, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 45, 45 ), 0 ); m_bitmapDifferent->SetToolTip( _("Items have different content") ); fgSizerSyncDirections->Add( m_bitmapDifferent, 0, wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 5 ); - m_bitmapConflict = new wxStaticBitmap( m_panelSyncSettings, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 45,45 ), 0 ); + m_bitmapConflict = new wxStaticBitmap( m_panelSyncSettings, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 45, 45 ), 0 ); m_bitmapConflict->SetToolTip( _("Conflict/item cannot be categorized") ); fgSizerSyncDirections->Add( m_bitmapConflict, 0, wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 5 ); - m_bitmapRightNewer = new wxStaticBitmap( m_panelSyncSettings, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 45,45 ), 0 ); + m_bitmapRightNewer = new wxStaticBitmap( m_panelSyncSettings, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 45, 45 ), 0 ); m_bitmapRightNewer->SetToolTip( _("Right side is newer") ); fgSizerSyncDirections->Add( m_bitmapRightNewer, 0, wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 5 ); - m_bitmapRightOnly = new wxStaticBitmap( m_panelSyncSettings, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 45,45 ), 0 ); + m_bitmapRightOnly = new wxStaticBitmap( m_panelSyncSettings, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 45, 45 ), 0 ); m_bitmapRightOnly->SetToolTip( _("Item exists on right side only") ); fgSizerSyncDirections->Add( m_bitmapRightOnly, 0, wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 5 ); - m_bpButtonLeftOnly = new wxBitmapButton( m_panelSyncSettings, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 46,46 ), wxBU_AUTODRAW ); + m_bpButtonLeftOnly = new wxBitmapButton( m_panelSyncSettings, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 46, 46 ), wxBU_AUTODRAW ); fgSizerSyncDirections->Add( m_bpButtonLeftOnly, 0, wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 5 ); - m_bpButtonLeftNewer = new wxBitmapButton( m_panelSyncSettings, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 46,46 ), wxBU_AUTODRAW ); + m_bpButtonLeftNewer = new wxBitmapButton( m_panelSyncSettings, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 46, 46 ), wxBU_AUTODRAW ); fgSizerSyncDirections->Add( m_bpButtonLeftNewer, 0, wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 5 ); - m_bpButtonDifferent = new wxBitmapButton( m_panelSyncSettings, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 46,46 ), wxBU_AUTODRAW ); + m_bpButtonDifferent = new wxBitmapButton( m_panelSyncSettings, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 46, 46 ), wxBU_AUTODRAW ); fgSizerSyncDirections->Add( m_bpButtonDifferent, 0, wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 5 ); - m_bpButtonConflict = new wxBitmapButton( m_panelSyncSettings, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 46,46 ), wxBU_AUTODRAW ); + m_bpButtonConflict = new wxBitmapButton( m_panelSyncSettings, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 46, 46 ), wxBU_AUTODRAW ); fgSizerSyncDirections->Add( m_bpButtonConflict, 0, wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 5 ); - m_bpButtonRightNewer = new wxBitmapButton( m_panelSyncSettings, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 46,46 ), wxBU_AUTODRAW ); + m_bpButtonRightNewer = new wxBitmapButton( m_panelSyncSettings, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 46, 46 ), wxBU_AUTODRAW ); fgSizerSyncDirections->Add( m_bpButtonRightNewer, 0, wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 5 ); - m_bpButtonRightOnly = new wxBitmapButton( m_panelSyncSettings, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 46,46 ), wxBU_AUTODRAW ); + m_bpButtonRightOnly = new wxBitmapButton( m_panelSyncSettings, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 46, 46 ), wxBU_AUTODRAW ); fgSizerSyncDirections->Add( m_bpButtonRightOnly, 0, wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 5 ); bSizerSyncConfig->Add( fgSizerSyncDirections, 0, 0, 5 ); - m_bitmapDatabase = new wxStaticBitmap( m_panelSyncSettings, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( -1,-1 ), 0 ); + m_bitmapDatabase = new wxStaticBitmap( m_panelSyncSettings, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( -1, -1 ), 0 ); m_bitmapDatabase->SetToolTip( _("Detect synchronization directions with the help of database files") ); bSizerSyncConfig->Add( m_bitmapDatabase, 0, wxALIGN_CENTER_HORIZONTAL|wxLEFT|wxALIGN_CENTER_VERTICAL, 10 ); @@ -1698,7 +1698,7 @@ ConfigDlgGenerated::ConfigDlgGenerated( wxWindow* parent, wxWindowID id, const w m_staticText87->Wrap( -1 ); bSizer202->Add( m_staticText87, 0, wxALL, 5 ); - m_bpButtonDeletionType = new wxBitmapButton( m_panelSyncSettings, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 46,46 ), wxBU_AUTODRAW ); + m_bpButtonDeletionType = new wxBitmapButton( m_panelSyncSettings, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 46, 46 ), wxBU_AUTODRAW ); bSizer202->Add( m_bpButtonDeletionType, 0, wxALIGN_CENTER_VERTICAL|wxALIGN_CENTER_HORIZONTAL|wxBOTTOM|wxRIGHT|wxLEFT, 5 ); @@ -1743,7 +1743,7 @@ ConfigDlgGenerated::ConfigDlgGenerated( wxWindow* parent, wxWindowID id, const w bSizer156->Add( m_buttonSelectVersioningFolder, 0, wxALIGN_CENTER_VERTICAL, 5 ); - m_bpButtonSelectAltFolder = new wxBitmapButton( m_panelVersioning, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 26,-1 ), wxBU_AUTODRAW ); + m_bpButtonSelectAltFolder = new wxBitmapButton( m_panelVersioning, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 26, -1 ), wxBU_AUTODRAW ); m_bpButtonSelectAltFolder->SetToolTip( _("Select SFTP folder") ); bSizer156->Add( m_bpButtonSelectAltFolder, 0, wxEXPAND, 5 ); @@ -1868,13 +1868,13 @@ ConfigDlgGenerated::ConfigDlgGenerated( wxWindow* parent, wxWindowID id, const w bSizerStdButtons = new wxBoxSizer( wxHORIZONTAL ); - m_buttonOkay = new wxButton( this, wxID_OK, _("OK"), wxDefaultPosition, wxSize( -1,-1 ), 0 ); + m_buttonOkay = new wxButton( this, wxID_OK, _("OK"), wxDefaultPosition, wxSize( -1, -1 ), 0 ); m_buttonOkay->SetDefault(); m_buttonOkay->SetFont( wxFont( wxNORMAL_FONT->GetPointSize(), 70, 90, 92, false, wxEmptyString ) ); bSizerStdButtons->Add( m_buttonOkay, 0, wxALIGN_CENTER_VERTICAL|wxALL, 5 ); - m_buttonCancel = new wxButton( this, wxID_CANCEL, _("Cancel"), wxDefaultPosition, wxSize( -1,-1 ), 0 ); + m_buttonCancel = new wxButton( this, wxID_CANCEL, _("Cancel"), wxDefaultPosition, wxSize( -1, -1 ), 0 ); bSizerStdButtons->Add( m_buttonCancel, 0, wxALIGN_CENTER_VERTICAL|wxTOP|wxBOTTOM|wxRIGHT, 5 ); @@ -1948,17 +1948,17 @@ FolderPairPanelGenerated::FolderPairPanelGenerated( wxWindow* parent, wxWindowID bSizer74 = new wxBoxSizer( wxHORIZONTAL ); m_panelLeft = new wxPanel( this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL ); - m_panelLeft->SetMinSize( wxSize( 1,-1 ) ); + m_panelLeft->SetMinSize( wxSize( 1, -1 ) ); wxBoxSizer* bSizer134; bSizer134 = new wxBoxSizer( wxHORIZONTAL ); - m_bpButtonFolderPairOptions = new wxBitmapButton( m_panelLeft, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 25,25 ), wxBU_AUTODRAW ); + m_bpButtonFolderPairOptions = new wxBitmapButton( m_panelLeft, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 25, 25 ), wxBU_AUTODRAW ); m_bpButtonFolderPairOptions->SetToolTip( _("Arrange folder pair") ); bSizer134->Add( m_bpButtonFolderPairOptions, 0, wxALIGN_CENTER_VERTICAL, 5 ); - m_bpButtonRemovePair = new wxBitmapButton( m_panelLeft, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 25,25 ), wxBU_AUTODRAW ); + m_bpButtonRemovePair = new wxBitmapButton( m_panelLeft, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 25, 25 ), wxBU_AUTODRAW ); m_bpButtonRemovePair->SetToolTip( _("Remove folder pair") ); bSizer134->Add( m_bpButtonRemovePair, 0, wxALIGN_CENTER_VERTICAL, 5 ); @@ -1971,7 +1971,7 @@ FolderPairPanelGenerated::FolderPairPanelGenerated( wxWindow* parent, wxWindowID bSizer134->Add( m_buttonSelectFolderLeft, 0, wxALIGN_CENTER_VERTICAL, 5 ); - m_bpButtonSelectAltFolderLeft = new wxBitmapButton( m_panelLeft, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 26,-1 ), wxBU_AUTODRAW ); + m_bpButtonSelectAltFolderLeft = new wxBitmapButton( m_panelLeft, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 26, -1 ), wxBU_AUTODRAW ); m_bpButtonSelectAltFolderLeft->SetToolTip( _("Select SFTP folder") ); bSizer134->Add( m_bpButtonSelectAltFolderLeft, 0, wxEXPAND, 5 ); @@ -1989,13 +1989,13 @@ FolderPairPanelGenerated::FolderPairPanelGenerated( wxWindow* parent, wxWindowID bSizer95->Add( 0, 0, 1, wxEXPAND, 5 ); - m_bpButtonAltCompCfg = new wxBitmapButton( m_panel20, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 26,25 ), wxBU_AUTODRAW ); + m_bpButtonAltCompCfg = new wxBitmapButton( m_panel20, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 26, 25 ), wxBU_AUTODRAW ); bSizer95->Add( m_bpButtonAltCompCfg, 0, wxALIGN_CENTER_VERTICAL, 5 ); - m_bpButtonLocalFilter = new wxBitmapButton( m_panel20, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 26,25 ), wxBU_AUTODRAW ); + m_bpButtonLocalFilter = new wxBitmapButton( m_panel20, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 26, 25 ), wxBU_AUTODRAW ); bSizer95->Add( m_bpButtonLocalFilter, 0, wxALIGN_CENTER_VERTICAL, 5 ); - m_bpButtonAltSyncCfg = new wxBitmapButton( m_panel20, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 26,25 ), wxBU_AUTODRAW ); + m_bpButtonAltSyncCfg = new wxBitmapButton( m_panel20, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 26, 25 ), wxBU_AUTODRAW ); bSizer95->Add( m_bpButtonAltSyncCfg, 0, wxALIGN_CENTER_VERTICAL, 5 ); @@ -2008,7 +2008,7 @@ FolderPairPanelGenerated::FolderPairPanelGenerated( wxWindow* parent, wxWindowID bSizer74->Add( m_panel20, 0, wxRIGHT|wxLEFT|wxEXPAND, 5 ); m_panelRight = new wxPanel( this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL ); - m_panelRight->SetMinSize( wxSize( 1,-1 ) ); + m_panelRight->SetMinSize( wxSize( 1, -1 ) ); wxBoxSizer* bSizer135; bSizer135 = new wxBoxSizer( wxHORIZONTAL ); @@ -2021,7 +2021,7 @@ FolderPairPanelGenerated::FolderPairPanelGenerated( wxWindow* parent, wxWindowID bSizer135->Add( m_buttonSelectFolderRight, 0, wxALIGN_CENTER_VERTICAL, 5 ); - m_bpButtonSelectAltFolderRight = new wxBitmapButton( m_panelRight, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 26,-1 ), wxBU_AUTODRAW ); + m_bpButtonSelectAltFolderRight = new wxBitmapButton( m_panelRight, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 26, -1 ), wxBU_AUTODRAW ); m_bpButtonSelectAltFolderRight->SetToolTip( _("Select SFTP folder") ); bSizer135->Add( m_bpButtonSelectAltFolderRight, 0, wxEXPAND, 5 ); @@ -2063,7 +2063,7 @@ SftpSetupDlgGenerated::SftpSetupDlgGenerated( wxWindow* parent, wxWindowID id, c bSizer134->Add( bSizer72, 0, 0, 5 ); - m_staticline371 = new wxStaticLine( this, wxID_ANY, wxDefaultPosition, wxSize( -1,-1 ), wxLI_HORIZONTAL ); + m_staticline371 = new wxStaticLine( this, wxID_ANY, wxDefaultPosition, wxSize( -1, -1 ), wxLI_HORIZONTAL ); bSizer134->Add( m_staticline371, 0, wxEXPAND, 5 ); m_panel41 = new wxPanel( this, wxID_ANY, wxDefaultPosition, wxDefaultSize, 0 ); @@ -2085,14 +2085,14 @@ SftpSetupDlgGenerated::SftpSetupDlgGenerated( wxWindow* parent, wxWindowID id, c wxBoxSizer* bSizer183; bSizer183 = new wxBoxSizer( wxHORIZONTAL ); - m_textCtrlServer = new wxTextCtrl( m_panel41, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize( 260,-1 ), 0 ); + m_textCtrlServer = new wxTextCtrl( m_panel41, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize( 260, -1 ), 0 ); bSizer183->Add( m_textCtrlServer, 1, wxALL|wxALIGN_CENTER_VERTICAL, 5 ); m_staticText1233 = new wxStaticText( m_panel41, wxID_ANY, _("Port:"), wxDefaultPosition, wxDefaultSize, 0 ); m_staticText1233->Wrap( -1 ); bSizer183->Add( m_staticText1233, 0, wxALIGN_CENTER_VERTICAL|wxTOP|wxBOTTOM|wxLEFT, 5 ); - m_textCtrlPort = new wxTextCtrl( m_panel41, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize( 60,-1 ), 0 ); + m_textCtrlPort = new wxTextCtrl( m_panel41, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize( 60, -1 ), 0 ); bSizer183->Add( m_textCtrlPort, 0, wxALL|wxALIGN_CENTER_VERTICAL, 5 ); @@ -2294,7 +2294,7 @@ SftpSetupDlgGenerated::SftpSetupDlgGenerated( wxWindow* parent, wxWindowID id, c m_staticText12341->Wrap( -1 ); fgSizer1611->Add( m_staticText12341, 0, wxTOP|wxBOTTOM|wxLEFT|wxALIGN_CENTER_VERTICAL|wxALIGN_RIGHT, 5 ); - m_spinCtrlConnectionCount = new wxSpinCtrl( m_panel411, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize( 70,-1 ), wxSP_ARROW_KEYS, 1, 2000000000, 1 ); + m_spinCtrlConnectionCount = new wxSpinCtrl( m_panel411, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize( 70, -1 ), wxSP_ARROW_KEYS, 1, 2000000000, 1 ); fgSizer1611->Add( m_spinCtrlConnectionCount, 0, wxALL, 5 ); m_staticText138111 = new wxStaticText( m_panel411, wxID_ANY, _("Suggested range: [1 - 10]"), wxDefaultPosition, wxDefaultSize, 0 ); @@ -2307,7 +2307,7 @@ SftpSetupDlgGenerated::SftpSetupDlgGenerated( wxWindow* parent, wxWindowID id, c m_staticText1231111->Wrap( -1 ); fgSizer1611->Add( m_staticText1231111, 0, wxALIGN_CENTER_VERTICAL|wxALIGN_RIGHT|wxTOP|wxBOTTOM|wxLEFT, 5 ); - m_spinCtrlChannelCount = new wxSpinCtrl( m_panel411, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize( 70,-1 ), wxSP_ARROW_KEYS, 1, 2000000000, 1 ); + m_spinCtrlChannelCount = new wxSpinCtrl( m_panel411, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize( 70, -1 ), wxSP_ARROW_KEYS, 1, 2000000000, 1 ); fgSizer1611->Add( m_spinCtrlChannelCount, 0, wxALL, 5 ); m_button42 = new wxButton( m_panel411, wxID_ANY, _("Detect server limit"), wxDefaultPosition, wxDefaultSize, 0 ); @@ -2345,13 +2345,13 @@ SftpSetupDlgGenerated::SftpSetupDlgGenerated( wxWindow* parent, wxWindowID id, c bSizerStdButtons = new wxBoxSizer( wxHORIZONTAL ); - m_buttonOkay = new wxButton( this, wxID_OK, _("OK"), wxDefaultPosition, wxSize( -1,-1 ), 0 ); + m_buttonOkay = new wxButton( this, wxID_OK, _("OK"), wxDefaultPosition, wxSize( -1, -1 ), 0 ); m_buttonOkay->SetDefault(); m_buttonOkay->SetFont( wxFont( wxNORMAL_FONT->GetPointSize(), 70, 90, 92, false, wxEmptyString ) ); bSizerStdButtons->Add( m_buttonOkay, 0, wxALL, 5 ); - m_buttonCancel = new wxButton( this, wxID_CANCEL, _("Cancel"), wxDefaultPosition, wxSize( -1,-1 ), 0 ); + m_buttonCancel = new wxButton( this, wxID_CANCEL, _("Cancel"), wxDefaultPosition, wxSize( -1, -1 ), 0 ); bSizerStdButtons->Add( m_buttonCancel, 0, wxALIGN_CENTER_VERTICAL|wxTOP|wxBOTTOM|wxRIGHT, 5 ); @@ -2402,7 +2402,7 @@ SftpFolderPickerGenerated::SftpFolderPickerGenerated( wxWindow* parent, wxWindow bSizer134->Add( bSizer72, 0, 0, 5 ); - m_staticline371 = new wxStaticLine( this, wxID_ANY, wxDefaultPosition, wxSize( -1,-1 ), wxLI_HORIZONTAL ); + m_staticline371 = new wxStaticLine( this, wxID_ANY, wxDefaultPosition, wxSize( -1, -1 ), wxLI_HORIZONTAL ); bSizer134->Add( m_staticline371, 0, wxEXPAND, 5 ); m_panel41 = new wxPanel( this, wxID_ANY, wxDefaultPosition, wxDefaultSize, 0 ); @@ -2411,7 +2411,7 @@ SftpFolderPickerGenerated::SftpFolderPickerGenerated( wxWindow* parent, wxWindow wxBoxSizer* bSizer185; bSizer185 = new wxBoxSizer( wxVERTICAL ); - m_treeCtrlFileSystem = new wxTreeCtrl( m_panel41, wxID_ANY, wxDefaultPosition, wxSize( 350,400 ), wxTR_FULL_ROW_HIGHLIGHT|wxTR_HAS_BUTTONS|wxTR_LINES_AT_ROOT|wxTR_NO_LINES|wxNO_BORDER ); + m_treeCtrlFileSystem = new wxTreeCtrl( m_panel41, wxID_ANY, wxDefaultPosition, wxSize( 350, 400 ), wxTR_FULL_ROW_HIGHLIGHT|wxTR_HAS_BUTTONS|wxTR_LINES_AT_ROOT|wxTR_NO_LINES|wxNO_BORDER ); bSizer185->Add( m_treeCtrlFileSystem, 1, wxEXPAND, 5 ); @@ -2425,13 +2425,13 @@ SftpFolderPickerGenerated::SftpFolderPickerGenerated( wxWindow* parent, wxWindow bSizerStdButtons = new wxBoxSizer( wxHORIZONTAL ); - m_buttonOkay = new wxButton( this, wxID_OK, _("Select Folder"), wxDefaultPosition, wxSize( -1,-1 ), 0 ); + m_buttonOkay = new wxButton( this, wxID_OK, _("Select Folder"), wxDefaultPosition, wxSize( -1, -1 ), 0 ); m_buttonOkay->SetDefault(); m_buttonOkay->SetFont( wxFont( wxNORMAL_FONT->GetPointSize(), 70, 90, 92, false, wxEmptyString ) ); bSizerStdButtons->Add( m_buttonOkay, 0, wxALL, 5 ); - m_buttonCancel = new wxButton( this, wxID_CANCEL, _("Cancel"), wxDefaultPosition, wxSize( -1,-1 ), 0 ); + m_buttonCancel = new wxButton( this, wxID_CANCEL, _("Cancel"), wxDefaultPosition, wxSize( -1, -1 ), 0 ); bSizerStdButtons->Add( m_buttonCancel, 0, wxALIGN_CENTER_VERTICAL|wxTOP|wxBOTTOM|wxRIGHT, 5 ); @@ -2646,13 +2646,13 @@ SyncConfirmationDlgGenerated::SyncConfirmationDlgGenerated( wxWindow* parent, wx bSizerStdButtons = new wxBoxSizer( wxHORIZONTAL ); - m_buttonStartSync = new wxButton( this, wxID_OK, _("Start"), wxDefaultPosition, wxSize( -1,-1 ), 0 ); + m_buttonStartSync = new wxButton( this, wxID_OK, _("Start"), wxDefaultPosition, wxSize( -1, -1 ), 0 ); m_buttonStartSync->SetDefault(); m_buttonStartSync->SetFont( wxFont( wxNORMAL_FONT->GetPointSize(), 70, 90, 92, false, wxEmptyString ) ); bSizerStdButtons->Add( m_buttonStartSync, 0, wxALIGN_CENTER_VERTICAL|wxBOTTOM|wxRIGHT|wxLEFT, 5 ); - m_buttonCancel = new wxButton( this, wxID_CANCEL, _("Cancel"), wxDefaultPosition, wxSize( -1,-1 ), 0 ); + m_buttonCancel = new wxButton( this, wxID_CANCEL, _("Cancel"), wxDefaultPosition, wxSize( -1, -1 ), 0 ); bSizerStdButtons->Add( m_buttonCancel, 0, wxALIGN_CENTER_VERTICAL|wxBOTTOM|wxRIGHT, 5 ); @@ -2793,7 +2793,7 @@ CompareProgressDlgGenerated::CompareProgressDlgGenerated( wxWindow* parent, wxWi bSizer201->Fit( m_panelProgressLabel ); bSizer199->Add( m_panelProgressLabel, 0, 0, 5 ); - m_panelGraphProgress = new zen::Graph2D( this, wxID_ANY, wxDefaultPosition, wxSize( -1,-1 ), 0 ); + m_panelGraphProgress = new zen::Graph2D( this, wxID_ANY, wxDefaultPosition, wxSize( -1, -1 ), 0 ); m_panelGraphProgress->SetBackgroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_BTNFACE ) ); bSizer199->Add( m_panelGraphProgress, 1, wxEXPAND, 5 ); @@ -2826,7 +2826,7 @@ SyncProgressPanelGenerated::SyncProgressPanelGenerated( wxWindow* parent, wxWind bSizer42->Add( 0, 0, 1, wxEXPAND, 5 ); - m_bitmapStatus = new wxStaticBitmap( this, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 32,32 ), 0 ); + m_bitmapStatus = new wxStaticBitmap( this, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 32, 32 ), 0 ); bSizer42->Add( m_bitmapStatus, 0, wxALIGN_CENTER_VERTICAL|wxALL, 2 ); m_staticTextPhase = new wxStaticText( this, wxID_ANY, _("Synchronizing..."), wxDefaultPosition, wxDefaultSize, 0 ); @@ -2835,13 +2835,13 @@ SyncProgressPanelGenerated::SyncProgressPanelGenerated( wxWindow* parent, wxWind bSizer42->Add( m_staticTextPhase, 0, wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL|wxTOP|wxBOTTOM, 5 ); - m_animCtrlSyncing = new wxAnimationCtrl( this, wxID_ANY, wxNullAnimation, wxDefaultPosition, wxSize( 32,32 ), wxAC_DEFAULT_STYLE ); + m_animCtrlSyncing = new wxAnimationCtrl( this, wxID_ANY, wxNullAnimation, wxDefaultPosition, wxSize( 32, 32 ), wxAC_DEFAULT_STYLE ); bSizer42->Add( m_animCtrlSyncing, 0, wxALIGN_CENTER_VERTICAL|wxALL, 2 ); bSizer42->Add( 0, 0, 1, wxEXPAND, 5 ); - m_bpButtonMinimizeToTray = new wxBitmapButton( this, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 32,32 ), wxBU_AUTODRAW ); + m_bpButtonMinimizeToTray = new wxBitmapButton( this, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 32, 32 ), wxBU_AUTODRAW ); m_bpButtonMinimizeToTray->SetToolTip( _("Minimize to notification area") ); bSizer42->Add( m_bpButtonMinimizeToTray, 0, wxALIGN_CENTER_VERTICAL, 5 ); @@ -2896,7 +2896,7 @@ SyncProgressPanelGenerated::SyncProgressPanelGenerated( wxWindow* parent, wxWind wxBoxSizer* bSizer169; bSizer169 = new wxBoxSizer( wxHORIZONTAL ); - m_staticTextItemsProcessed = new wxStaticText( m_panelItemsProcessed, wxID_ANY, _("dummy"), wxDefaultPosition, wxSize( -1,-1 ), 0 ); + m_staticTextItemsProcessed = new wxStaticText( m_panelItemsProcessed, wxID_ANY, _("dummy"), wxDefaultPosition, wxSize( -1, -1 ), 0 ); m_staticTextItemsProcessed->Wrap( -1 ); m_staticTextItemsProcessed->SetFont( wxFont( wxNORMAL_FONT->GetPointSize(), 70, 90, 92, false, wxEmptyString ) ); @@ -2935,7 +2935,7 @@ SyncProgressPanelGenerated::SyncProgressPanelGenerated( wxWindow* parent, wxWind wxBoxSizer* bSizer170; bSizer170 = new wxBoxSizer( wxHORIZONTAL ); - m_staticTextItemsRemaining = new wxStaticText( m_panelItemsRemaining, wxID_ANY, _("dummy"), wxDefaultPosition, wxSize( -1,-1 ), 0 ); + m_staticTextItemsRemaining = new wxStaticText( m_panelItemsRemaining, wxID_ANY, _("dummy"), wxDefaultPosition, wxSize( -1, -1 ), 0 ); m_staticTextItemsRemaining->Wrap( -1 ); m_staticTextItemsRemaining->SetFont( wxFont( wxNORMAL_FONT->GetPointSize(), 70, 90, 92, false, wxEmptyString ) ); @@ -3039,7 +3039,7 @@ SyncProgressPanelGenerated::SyncProgressPanelGenerated( wxWindow* parent, wxWind bSizer161->Add( bSizer175, 0, 0, 5 ); - m_panelGraphBytes = new zen::Graph2D( m_panelProgress, wxID_ANY, wxDefaultPosition, wxSize( -1,-1 ), 0 ); + m_panelGraphBytes = new zen::Graph2D( m_panelProgress, wxID_ANY, wxDefaultPosition, wxSize( -1, -1 ), 0 ); m_panelGraphBytes->SetBackgroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_WINDOW ) ); bSizer161->Add( m_panelGraphBytes, 1, wxEXPAND, 15 ); @@ -3058,7 +3058,7 @@ SyncProgressPanelGenerated::SyncProgressPanelGenerated( wxWindow* parent, wxWind bSizer161->Add( bSizer174, 0, 0, 5 ); - m_panelGraphItems = new zen::Graph2D( m_panelProgress, wxID_ANY, wxDefaultPosition, wxSize( -1,-1 ), 0 ); + m_panelGraphItems = new zen::Graph2D( m_panelProgress, wxID_ANY, wxDefaultPosition, wxSize( -1, -1 ), 0 ); m_panelGraphItems->SetBackgroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_WINDOW ) ); bSizer161->Add( m_panelGraphItems, 1, wxEXPAND, 15 ); @@ -3111,16 +3111,16 @@ SyncProgressPanelGenerated::SyncProgressPanelGenerated( wxWindow* parent, wxWind bSizerStdButtons->Add( bSizer160, 1, wxALIGN_CENTER_VERTICAL|wxLEFT, 5 ); - m_buttonClose = new wxButton( this, wxID_OK, _("Close"), wxDefaultPosition, wxSize( -1,-1 ), 0 ); + m_buttonClose = new wxButton( this, wxID_OK, _("Close"), wxDefaultPosition, wxSize( -1, -1 ), 0 ); m_buttonClose->SetDefault(); m_buttonClose->Enable( false ); bSizerStdButtons->Add( m_buttonClose, 0, wxALIGN_CENTER_VERTICAL|wxALL, 5 ); - m_buttonPause = new wxButton( this, wxID_ANY, _("&Pause"), wxDefaultPosition, wxSize( -1,-1 ), 0 ); + m_buttonPause = new wxButton( this, wxID_ANY, _("&Pause"), wxDefaultPosition, wxSize( -1, -1 ), 0 ); bSizerStdButtons->Add( m_buttonPause, 0, wxALIGN_CENTER_VERTICAL|wxTOP|wxBOTTOM|wxRIGHT, 5 ); - m_buttonStop = new wxButton( this, wxID_CANCEL, _("Stop"), wxDefaultPosition, wxSize( -1,-1 ), 0 ); + m_buttonStop = new wxButton( this, wxID_CANCEL, _("Stop"), wxDefaultPosition, wxSize( -1, -1 ), 0 ); bSizerStdButtons->Add( m_buttonStop, 0, wxALIGN_CENTER_VERTICAL|wxTOP|wxBOTTOM|wxRIGHT, 5 ); @@ -3149,13 +3149,13 @@ LogPanelGenerated::LogPanelGenerated( wxWindow* parent, wxWindowID id, const wxP wxBoxSizer* bSizer154; bSizer154 = new wxBoxSizer( wxVERTICAL ); - m_bpButtonErrors = new ToggleButton( this, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 49,49 ), wxBU_AUTODRAW ); + m_bpButtonErrors = new ToggleButton( this, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 49, 49 ), wxBU_AUTODRAW ); bSizer154->Add( m_bpButtonErrors, 0, wxALIGN_CENTER_HORIZONTAL, 5 ); - m_bpButtonWarnings = new ToggleButton( this, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 49,49 ), wxBU_AUTODRAW ); + m_bpButtonWarnings = new ToggleButton( this, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 49, 49 ), wxBU_AUTODRAW ); bSizer154->Add( m_bpButtonWarnings, 0, wxALIGN_CENTER_HORIZONTAL, 5 ); - m_bpButtonInfo = new ToggleButton( this, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 49,49 ), wxBU_AUTODRAW ); + m_bpButtonInfo = new ToggleButton( this, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 49, 49 ), wxBU_AUTODRAW ); bSizer154->Add( m_bpButtonInfo, 0, wxALIGN_CENTER_HORIZONTAL, 5 ); @@ -3188,7 +3188,7 @@ LogPanelGenerated::~LogPanelGenerated() BatchDlgGenerated::BatchDlgGenerated( wxWindow* parent, wxWindowID id, const wxString& title, const wxPoint& pos, const wxSize& size, long style ) : wxDialog( parent, id, title, pos, size, style ) { - this->SetSizeHints( wxSize( -1,-1 ), wxDefaultSize ); + this->SetSizeHints( wxSize( -1, -1 ), wxDefaultSize ); this->SetBackgroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_BTNFACE ) ); wxBoxSizer* bSizer54; @@ -3197,7 +3197,7 @@ BatchDlgGenerated::BatchDlgGenerated( wxWindow* parent, wxWindowID id, const wxS wxBoxSizer* bSizer72; bSizer72 = new wxBoxSizer( wxHORIZONTAL ); - m_bitmapBatchJob = new wxStaticBitmap( this, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( -1,-1 ), 0 ); + m_bitmapBatchJob = new wxStaticBitmap( this, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( -1, -1 ), 0 ); bSizer72->Add( m_bitmapBatchJob, 0, wxALIGN_CENTER_VERTICAL|wxTOP|wxBOTTOM|wxLEFT, 10 ); m_staticTextDescr = new wxStaticText( this, wxID_ANY, _("Create a batch file for unattended synchronization. To start, double-click this file or schedule in a task planner: %x"), wxDefaultPosition, wxDefaultSize, 0 ); @@ -3290,7 +3290,7 @@ BatchDlgGenerated::BatchDlgGenerated( wxWindow* parent, wxWindowID id, const wxS bSizer1721->Add( m_buttonSelectLogFolder, 0, wxALIGN_CENTER_VERTICAL, 5 ); - m_bpButtonSelectAltLogFolder = new wxBitmapButton( m_panelLogfile, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 26,-1 ), wxBU_AUTODRAW ); + m_bpButtonSelectAltLogFolder = new wxBitmapButton( m_panelLogfile, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 26, -1 ), wxBU_AUTODRAW ); m_bpButtonSelectAltLogFolder->SetToolTip( _("Select SFTP folder") ); bSizer1721->Add( m_bpButtonSelectAltLogFolder, 0, wxEXPAND, 5 ); @@ -3300,7 +3300,7 @@ BatchDlgGenerated::BatchDlgGenerated( wxWindow* parent, wxWindowID id, const wxS bSizer1721->Add( m_checkBoxLogfilesLimit, 0, wxALL|wxALIGN_CENTER_VERTICAL, 5 ); - m_spinCtrlLogfileLimit = new wxSpinCtrl( m_panelLogfile, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize( 70,-1 ), wxSP_ARROW_KEYS, 1, 2000000000, 1 ); + m_spinCtrlLogfileLimit = new wxSpinCtrl( m_panelLogfile, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize( 70, -1 ), wxSP_ARROW_KEYS, 1, 2000000000, 1 ); m_spinCtrlLogfileLimit->SetToolTip( _("Limit maximum number of log files") ); bSizer1721->Add( m_spinCtrlLogfileLimit, 0, wxALIGN_CENTER_VERTICAL, 5 ); @@ -3328,13 +3328,13 @@ BatchDlgGenerated::BatchDlgGenerated( wxWindow* parent, wxWindowID id, const wxS bSizerStdButtons = new wxBoxSizer( wxHORIZONTAL ); - m_buttonSaveAs = new wxButton( this, wxID_SAVE, _("Save &as..."), wxDefaultPosition, wxSize( -1,-1 ), 0 ); + m_buttonSaveAs = new wxButton( this, wxID_SAVE, _("Save &as..."), wxDefaultPosition, wxSize( -1, -1 ), 0 ); m_buttonSaveAs->SetDefault(); m_buttonSaveAs->SetFont( wxFont( wxNORMAL_FONT->GetPointSize(), 70, 90, 92, false, wxEmptyString ) ); bSizerStdButtons->Add( m_buttonSaveAs, 0, wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL|wxALL, 5 ); - m_buttonCancel = new wxButton( this, wxID_CANCEL, _("Cancel"), wxDefaultPosition, wxSize( -1,-1 ), 0 ); + m_buttonCancel = new wxButton( this, wxID_CANCEL, _("Cancel"), wxDefaultPosition, wxSize( -1, -1 ), 0 ); bSizerStdButtons->Add( m_buttonCancel, 0, wxALIGN_CENTER_VERTICAL|wxTOP|wxBOTTOM|wxRIGHT, 5 ); @@ -3365,7 +3365,7 @@ BatchDlgGenerated::~BatchDlgGenerated() DeleteDlgGenerated::DeleteDlgGenerated( wxWindow* parent, wxWindowID id, const wxString& title, const wxPoint& pos, const wxSize& size, long style ) : wxDialog( parent, id, title, pos, size, style ) { - this->SetSizeHints( wxSize( -1,-1 ), wxDefaultSize ); + this->SetSizeHints( wxSize( -1, -1 ), wxDefaultSize ); this->SetBackgroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_BTNFACE ) ); wxBoxSizer* bSizer24; @@ -3399,7 +3399,7 @@ DeleteDlgGenerated::DeleteDlgGenerated( wxWindow* parent, wxWindowID id, const w m_staticline42 = new wxStaticLine( m_panel31, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxLI_VERTICAL ); bSizer185->Add( m_staticline42, 0, wxEXPAND, 5 ); - m_textCtrlFileList = new wxTextCtrl( m_panel31, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize( 500,200 ), wxTE_DONTWRAP|wxTE_MULTILINE|wxTE_READONLY|wxNO_BORDER ); + m_textCtrlFileList = new wxTextCtrl( m_panel31, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize( 500, 200 ), wxTE_DONTWRAP|wxTE_MULTILINE|wxTE_READONLY|wxNO_BORDER ); bSizer185->Add( m_textCtrlFileList, 1, wxEXPAND, 5 ); @@ -3416,13 +3416,13 @@ DeleteDlgGenerated::DeleteDlgGenerated( wxWindow* parent, wxWindowID id, const w m_checkBoxUseRecycler = new wxCheckBox( this, wxID_ANY, _("&Recycle bin"), wxDefaultPosition, wxDefaultSize, 0 ); bSizerStdButtons->Add( m_checkBoxUseRecycler, 1, wxALIGN_CENTER_VERTICAL|wxALL, 5 ); - m_buttonOK = new wxButton( this, wxID_OK, _("dummy"), wxDefaultPosition, wxSize( -1,-1 ), 0 ); + m_buttonOK = new wxButton( this, wxID_OK, _("dummy"), wxDefaultPosition, wxSize( -1, -1 ), 0 ); m_buttonOK->SetDefault(); m_buttonOK->SetFont( wxFont( wxNORMAL_FONT->GetPointSize(), 70, 90, 92, false, wxEmptyString ) ); bSizerStdButtons->Add( m_buttonOK, 0, wxALIGN_CENTER_VERTICAL|wxTOP|wxBOTTOM|wxRIGHT, 5 ); - m_buttonCancel = new wxButton( this, wxID_CANCEL, _("Cancel"), wxDefaultPosition, wxSize( -1,-1 ), 0 ); + m_buttonCancel = new wxButton( this, wxID_CANCEL, _("Cancel"), wxDefaultPosition, wxSize( -1, -1 ), 0 ); bSizerStdButtons->Add( m_buttonCancel, 0, wxALIGN_CENTER_VERTICAL|wxTOP|wxBOTTOM|wxRIGHT, 5 ); @@ -3448,7 +3448,7 @@ DeleteDlgGenerated::~DeleteDlgGenerated() CopyToDlgGenerated::CopyToDlgGenerated( wxWindow* parent, wxWindowID id, const wxString& title, const wxPoint& pos, const wxSize& size, long style ) : wxDialog( parent, id, title, pos, size, style ) { - this->SetSizeHints( wxSize( -1,-1 ), wxDefaultSize ); + this->SetSizeHints( wxSize( -1, -1 ), wxDefaultSize ); this->SetBackgroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_BTNFACE ) ); wxBoxSizer* bSizer24; @@ -3482,7 +3482,7 @@ CopyToDlgGenerated::CopyToDlgGenerated( wxWindow* parent, wxWindowID id, const w m_staticline42 = new wxStaticLine( m_panel31, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxLI_VERTICAL ); bSizer185->Add( m_staticline42, 0, wxEXPAND, 5 ); - m_textCtrlFileList = new wxTextCtrl( m_panel31, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize( 500,200 ), wxTE_DONTWRAP|wxTE_MULTILINE|wxTE_READONLY|wxNO_BORDER ); + m_textCtrlFileList = new wxTextCtrl( m_panel31, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize( 500, 200 ), wxTE_DONTWRAP|wxTE_MULTILINE|wxTE_READONLY|wxNO_BORDER ); bSizer185->Add( m_textCtrlFileList, 1, wxEXPAND, 5 ); @@ -3502,7 +3502,7 @@ CopyToDlgGenerated::CopyToDlgGenerated( wxWindow* parent, wxWindowID id, const w bSizer182->Add( m_buttonSelectTargetFolder, 0, wxALIGN_CENTER_VERTICAL, 5 ); - m_bpButtonSelectAltTargetFolder = new wxBitmapButton( this, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 26,-1 ), wxBU_AUTODRAW ); + m_bpButtonSelectAltTargetFolder = new wxBitmapButton( this, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 26, -1 ), wxBU_AUTODRAW ); m_bpButtonSelectAltTargetFolder->SetToolTip( _("Select SFTP folder") ); bSizer182->Add( m_bpButtonSelectAltTargetFolder, 0, wxEXPAND, 5 ); @@ -3529,13 +3529,13 @@ CopyToDlgGenerated::CopyToDlgGenerated( wxWindow* parent, wxWindowID id, const w bSizerStdButtons->Add( bSizer189, 1, wxALIGN_CENTER_VERTICAL, 5 ); - m_buttonOK = new wxButton( this, wxID_OK, _("Copy"), wxDefaultPosition, wxSize( -1,-1 ), 0 ); + m_buttonOK = new wxButton( this, wxID_OK, _("Copy"), wxDefaultPosition, wxSize( -1, -1 ), 0 ); m_buttonOK->SetDefault(); m_buttonOK->SetFont( wxFont( wxNORMAL_FONT->GetPointSize(), 70, 90, 92, false, wxEmptyString ) ); bSizerStdButtons->Add( m_buttonOK, 0, wxALIGN_CENTER_VERTICAL|wxTOP|wxBOTTOM|wxRIGHT, 5 ); - m_buttonCancel = new wxButton( this, wxID_CANCEL, _("Cancel"), wxDefaultPosition, wxSize( -1,-1 ), 0 ); + m_buttonCancel = new wxButton( this, wxID_CANCEL, _("Cancel"), wxDefaultPosition, wxSize( -1, -1 ), 0 ); bSizerStdButtons->Add( m_buttonCancel, 0, wxALIGN_CENTER_VERTICAL|wxTOP|wxBOTTOM|wxRIGHT, 5 ); @@ -3562,7 +3562,7 @@ CopyToDlgGenerated::~CopyToDlgGenerated() OptionsDlgGenerated::OptionsDlgGenerated( wxWindow* parent, wxWindowID id, const wxString& title, const wxPoint& pos, const wxSize& size, long style ) : wxDialog( parent, id, title, pos, size, style ) { - this->SetSizeHints( wxSize( -1,-1 ), wxDefaultSize ); + this->SetSizeHints( wxSize( -1, -1 ), wxDefaultSize ); this->SetBackgroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_BTNFACE ) ); wxBoxSizer* bSizer95; @@ -3571,10 +3571,10 @@ OptionsDlgGenerated::OptionsDlgGenerated( wxWindow* parent, wxWindowID id, const wxBoxSizer* bSizer72; bSizer72 = new wxBoxSizer( wxHORIZONTAL ); - m_bitmapSettings = new wxStaticBitmap( this, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( -1,-1 ), 0 ); + m_bitmapSettings = new wxStaticBitmap( this, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( -1, -1 ), 0 ); bSizer72->Add( m_bitmapSettings, 0, wxALIGN_CENTER_VERTICAL|wxTOP|wxBOTTOM|wxLEFT, 10 ); - m_staticText44 = new wxStaticText( this, wxID_ANY, _("The following settings are used for all synchronization jobs."), wxDefaultPosition, wxSize( -1,-1 ), 0 ); + m_staticText44 = new wxStaticText( this, wxID_ANY, _("The following settings are used for all synchronization jobs."), wxDefaultPosition, wxSize( -1, -1 ), 0 ); m_staticText44->Wrap( 500 ); bSizer72->Add( m_staticText44, 0, wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL|wxALL, 10 ); @@ -3671,14 +3671,14 @@ OptionsDlgGenerated::OptionsDlgGenerated( wxWindow* parent, wxWindowID id, const m_staticText96->Wrap( -1 ); fgSizer6->Add( m_staticText96, 0, wxALIGN_CENTER_VERTICAL, 5 ); - m_spinCtrlAutoRetryCount = new wxSpinCtrl( m_panel39, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize( 60,-1 ), wxSP_ARROW_KEYS, 0, 2000000000, 4 ); + m_spinCtrlAutoRetryCount = new wxSpinCtrl( m_panel39, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize( 60, -1 ), wxSP_ARROW_KEYS, 0, 2000000000, 4 ); fgSizer6->Add( m_spinCtrlAutoRetryCount, 0, wxALIGN_CENTER_VERTICAL, 5 ); m_staticTextAutoRetryDelay = new wxStaticText( m_panel39, wxID_ANY, _("Delay (in seconds):"), wxDefaultPosition, wxDefaultSize, 0 ); m_staticTextAutoRetryDelay->Wrap( -1 ); fgSizer6->Add( m_staticTextAutoRetryDelay, 0, wxALIGN_CENTER_VERTICAL, 5 ); - m_spinCtrlAutoRetryDelay = new wxSpinCtrl( m_panel39, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize( 60,-1 ), wxSP_ARROW_KEYS, 0, 2000000000, 0 ); + m_spinCtrlAutoRetryDelay = new wxSpinCtrl( m_panel39, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize( 60, -1 ), wxSP_ARROW_KEYS, 0, 2000000000, 0 ); fgSizer6->Add( m_spinCtrlAutoRetryDelay, 0, wxALIGN_CENTER_VERTICAL, 5 ); @@ -3733,10 +3733,10 @@ OptionsDlgGenerated::OptionsDlgGenerated( wxWindow* parent, wxWindowID id, const wxBoxSizer* bSizer193; bSizer193 = new wxBoxSizer( wxHORIZONTAL ); - m_bpButtonAddRow = new wxBitmapButton( m_panel39, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 25,25 ), wxBU_AUTODRAW ); + m_bpButtonAddRow = new wxBitmapButton( m_panel39, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 25, 25 ), wxBU_AUTODRAW ); bSizer193->Add( m_bpButtonAddRow, 0, 0, 5 ); - m_bpButtonRemoveRow = new wxBitmapButton( m_panel39, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 25,25 ), wxBU_AUTODRAW ); + m_bpButtonRemoveRow = new wxBitmapButton( m_panel39, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 25, 25 ), wxBU_AUTODRAW ); bSizer193->Add( m_bpButtonRemoveRow, 0, 0, 5 ); @@ -3757,7 +3757,7 @@ OptionsDlgGenerated::OptionsDlgGenerated( wxWindow* parent, wxWindowID id, const wxBoxSizer* bSizer1881; bSizer1881 = new wxBoxSizer( wxHORIZONTAL ); - m_buttonResetDialogs = new zen::BitmapTextButton( m_panel39, wxID_ANY, _("Show hidden dialogs again"), wxDefaultPosition, wxSize( -1,-1 ), 0 ); + m_buttonResetDialogs = new zen::BitmapTextButton( m_panel39, wxID_ANY, _("Show hidden dialogs again"), wxDefaultPosition, wxSize( -1, -1 ), 0 ); m_buttonResetDialogs->SetToolTip( _("Show all permanently hidden dialogs and warning messages again") ); bSizer1881->Add( m_buttonResetDialogs, 0, wxALL|wxALIGN_CENTER_VERTICAL, 10 ); @@ -3779,19 +3779,19 @@ OptionsDlgGenerated::OptionsDlgGenerated( wxWindow* parent, wxWindowID id, const bSizerStdButtons = new wxBoxSizer( wxHORIZONTAL ); - m_buttonDefault = new wxButton( this, wxID_DEFAULT, _("&Default"), wxDefaultPosition, wxSize( -1,-1 ), 0 ); + m_buttonDefault = new wxButton( this, wxID_DEFAULT, _("&Default"), wxDefaultPosition, wxSize( -1, -1 ), 0 ); bSizerStdButtons->Add( m_buttonDefault, 0, wxALL|wxALIGN_CENTER_VERTICAL, 5 ); bSizerStdButtons->Add( 0, 0, 1, 0, 5 ); - m_buttonOkay = new wxButton( this, wxID_OK, _("OK"), wxDefaultPosition, wxSize( -1,-1 ), 0 ); + m_buttonOkay = new wxButton( this, wxID_OK, _("OK"), wxDefaultPosition, wxSize( -1, -1 ), 0 ); m_buttonOkay->SetDefault(); m_buttonOkay->SetFont( wxFont( wxNORMAL_FONT->GetPointSize(), 70, 90, 92, false, wxEmptyString ) ); bSizerStdButtons->Add( m_buttonOkay, 0, wxALIGN_CENTER_VERTICAL|wxALL, 5 ); - m_buttonCancel = new wxButton( this, wxID_CANCEL, _("Cancel"), wxDefaultPosition, wxSize( -1,-1 ), 0 ); + m_buttonCancel = new wxButton( this, wxID_CANCEL, _("Cancel"), wxDefaultPosition, wxSize( -1, -1 ), 0 ); bSizerStdButtons->Add( m_buttonCancel, 0, wxALIGN_CENTER_VERTICAL|wxTOP|wxBOTTOM|wxRIGHT, 5 ); @@ -3875,13 +3875,13 @@ SelectTimespanDlgGenerated::SelectTimespanDlgGenerated( wxWindow* parent, wxWind bSizerStdButtons = new wxBoxSizer( wxHORIZONTAL ); - m_buttonOkay = new wxButton( this, wxID_OK, _("OK"), wxDefaultPosition, wxSize( -1,-1 ), 0 ); + m_buttonOkay = new wxButton( this, wxID_OK, _("OK"), wxDefaultPosition, wxSize( -1, -1 ), 0 ); m_buttonOkay->SetDefault(); m_buttonOkay->SetFont( wxFont( wxNORMAL_FONT->GetPointSize(), 70, 90, 92, false, wxEmptyString ) ); bSizerStdButtons->Add( m_buttonOkay, 0, wxALIGN_CENTER_VERTICAL|wxALL, 5 ); - m_buttonCancel = new wxButton( this, wxID_CANCEL, _("Cancel"), wxDefaultPosition, wxSize( -1,-1 ), 0 ); + m_buttonCancel = new wxButton( this, wxID_CANCEL, _("Cancel"), wxDefaultPosition, wxSize( -1, -1 ), 0 ); bSizerStdButtons->Add( m_buttonCancel, 0, wxALIGN_CENTER_VERTICAL|wxTOP|wxBOTTOM|wxRIGHT, 5 ); @@ -3920,7 +3920,7 @@ AboutDlgGenerated::AboutDlgGenerated( wxWindow* parent, wxWindowID id, const wxS wxBoxSizer* bSizer162; bSizer162 = new wxBoxSizer( wxVERTICAL ); - m_bitmapLogo = new wxStaticBitmap( m_panel41, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( -1,-1 ), 0 ); + m_bitmapLogo = new wxStaticBitmap( m_panel41, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( -1, -1 ), 0 ); bSizer162->Add( m_bitmapLogo, 0, 0, 5 ); m_staticline341 = new wxStaticLine( m_panel41, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxLI_HORIZONTAL ); @@ -4130,7 +4130,7 @@ AboutDlgGenerated::AboutDlgGenerated( wxWindow* parent, wxWindowID id, const wxS bSizer166->Add( 0, 0, 1, wxEXPAND, 5 ); - m_bitmapHomepage = new wxStaticBitmap( m_panel41, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( -1,-1 ), 0 ); + m_bitmapHomepage = new wxStaticBitmap( m_panel41, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( -1, -1 ), 0 ); m_bitmapHomepage->SetToolTip( _("Home page") ); bSizer166->Add( m_bitmapHomepage, 0, wxALIGN_CENTER_VERTICAL|wxRIGHT|wxLEFT, 5 ); @@ -4145,7 +4145,7 @@ AboutDlgGenerated::AboutDlgGenerated( wxWindow* parent, wxWindowID id, const wxS bSizer166->Add( 0, 0, 1, wxEXPAND, 5 ); - m_bitmapEmail = new wxStaticBitmap( m_panel41, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( -1,-1 ), 0 ); + m_bitmapEmail = new wxStaticBitmap( m_panel41, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( -1, -1 ), 0 ); m_bitmapEmail->SetToolTip( _("Email") ); bSizer166->Add( m_bitmapEmail, 0, wxALIGN_CENTER_VERTICAL|wxRIGHT|wxLEFT, 5 ); @@ -4179,7 +4179,7 @@ AboutDlgGenerated::AboutDlgGenerated( wxWindow* parent, wxWindowID id, const wxS wxBoxSizer* bSizer1671; bSizer1671 = new wxBoxSizer( wxHORIZONTAL ); - m_bitmapGpl = new wxStaticBitmap( m_panel41, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( -1,-1 ), 0 ); + m_bitmapGpl = new wxStaticBitmap( m_panel41, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( -1, -1 ), 0 ); bSizer1671->Add( m_bitmapGpl, 0, wxALIGN_CENTER_VERTICAL|wxRIGHT, 5 ); m_hyperlink5 = new wxHyperlinkCtrl( m_panel41, wxID_ANY, _("http://www.gnu.org/licenses/gpl-3.0"), wxT("http://www.gnu.org/licenses/gpl-3.0"), wxDefaultPosition, wxDefaultSize, wxHL_DEFAULT_STYLE ); @@ -4211,10 +4211,10 @@ AboutDlgGenerated::AboutDlgGenerated( wxWindow* parent, wxWindowID id, const wxS bSizer177->Add( 0, 5, 0, 0, 5 ); - m_scrolledWindowTranslators = new wxScrolledWindow( m_panel41, wxID_ANY, wxDefaultPosition, wxSize( -1,-1 ), wxVSCROLL ); + m_scrolledWindowTranslators = new wxScrolledWindow( m_panel41, wxID_ANY, wxDefaultPosition, wxSize( -1, -1 ), wxVSCROLL ); m_scrolledWindowTranslators->SetScrollRate( 10, 10 ); m_scrolledWindowTranslators->SetBackgroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_WINDOW ) ); - m_scrolledWindowTranslators->SetMinSize( wxSize( 220,-1 ) ); + m_scrolledWindowTranslators->SetMinSize( wxSize( 220, -1 ) ); fgSizerTranslators = new wxFlexGridSizer( 0, 2, 2, 10 ); fgSizerTranslators->SetFlexibleDirection( wxBOTH ); @@ -4243,7 +4243,7 @@ AboutDlgGenerated::AboutDlgGenerated( wxWindow* parent, wxWindowID id, const wxS bSizerStdButtons = new wxBoxSizer( wxHORIZONTAL ); - m_buttonClose = new wxButton( this, wxID_OK, _("Close"), wxDefaultPosition, wxSize( -1,-1 ), 0 ); + m_buttonClose = new wxButton( this, wxID_OK, _("Close"), wxDefaultPosition, wxSize( -1, -1 ), 0 ); m_buttonClose->SetDefault(); bSizerStdButtons->Add( m_buttonClose, 0, wxALL|wxALIGN_CENTER_VERTICAL, 5 ); @@ -4310,7 +4310,7 @@ DownloadProgressDlgGenerated::DownloadProgressDlgGenerated( wxWindow* parent, wx bSizerStdButtons = new wxBoxSizer( wxHORIZONTAL ); - m_buttonCancel = new wxButton( this, wxID_CANCEL, _("Cancel"), wxDefaultPosition, wxSize( -1,-1 ), 0 ); + m_buttonCancel = new wxButton( this, wxID_CANCEL, _("Cancel"), wxDefaultPosition, wxSize( -1, -1 ), 0 ); m_buttonCancel->SetDefault(); bSizerStdButtons->Add( m_buttonCancel, 0, wxALIGN_CENTER_VERTICAL|wxALL, 5 ); @@ -4332,7 +4332,7 @@ DownloadProgressDlgGenerated::~DownloadProgressDlgGenerated() ActivationDlgGenerated::ActivationDlgGenerated( wxWindow* parent, wxWindowID id, const wxString& title, const wxPoint& pos, const wxSize& size, long style ) : wxDialog( parent, id, title, pos, size, style ) { - this->SetSizeHints( wxSize( -1,-1 ), wxDefaultSize ); + this->SetSizeHints( wxSize( -1, -1 ), wxDefaultSize ); this->SetBackgroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_BTNFACE ) ); wxBoxSizer* bSizer54; @@ -4347,7 +4347,7 @@ ActivationDlgGenerated::ActivationDlgGenerated( wxWindow* parent, wxWindowID id, wxBoxSizer* bSizer165; bSizer165 = new wxBoxSizer( wxHORIZONTAL ); - m_bitmapActivation = new wxStaticBitmap( m_panel35, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( -1,-1 ), 0 ); + m_bitmapActivation = new wxStaticBitmap( m_panel35, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( -1, -1 ), 0 ); bSizer165->Add( m_bitmapActivation, 0, wxALL, 10 ); @@ -4400,7 +4400,7 @@ ActivationDlgGenerated::ActivationDlgGenerated( wxWindow* parent, wxWindowID id, m_staticText136->Wrap( -1 ); bSizer234->Add( m_staticText136, 1, wxALIGN_CENTER_VERTICAL|wxTOP|wxBOTTOM|wxLEFT, 5 ); - m_buttonActivateOnline = new wxButton( m_panel3511, wxID_ANY, _("Activate online"), wxDefaultPosition, wxSize( -1,-1 ), 0 ); + m_buttonActivateOnline = new wxButton( m_panel3511, wxID_ANY, _("Activate online"), wxDefaultPosition, wxSize( -1, -1 ), 0 ); m_buttonActivateOnline->SetDefault(); m_buttonActivateOnline->SetFont( wxFont( wxNORMAL_FONT->GetPointSize(), 70, 90, 92, false, wxEmptyString ) ); @@ -4437,7 +4437,7 @@ ActivationDlgGenerated::ActivationDlgGenerated( wxWindow* parent, wxWindowID id, m_staticText1361->Wrap( -1 ); bSizer236->Add( m_staticText1361, 1, wxALIGN_CENTER_VERTICAL|wxTOP|wxBOTTOM|wxLEFT, 5 ); - m_buttonCopyUrl = new wxButton( m_panel351, wxID_ANY, _("&Copy to clipboard"), wxDefaultPosition, wxSize( -1,-1 ), 0 ); + m_buttonCopyUrl = new wxButton( m_panel351, wxID_ANY, _("&Copy to clipboard"), wxDefaultPosition, wxSize( -1, -1 ), 0 ); m_buttonCopyUrl->SetDefault(); m_buttonCopyUrl->SetFont( wxFont( wxNORMAL_FONT->GetPointSize(), 70, 90, 90, false, wxEmptyString ) ); @@ -4446,7 +4446,7 @@ ActivationDlgGenerated::ActivationDlgGenerated( wxWindow* parent, wxWindowID id, bSizer237->Add( bSizer236, 0, wxEXPAND, 5 ); - m_textCtrlManualActivationUrl = new wxTextCtrl( m_panel351, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize( 220,-1 ), wxTE_MULTILINE|wxTE_READONLY|wxWANTS_CHARS ); + m_textCtrlManualActivationUrl = new wxTextCtrl( m_panel351, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize( 220, -1 ), wxTE_MULTILINE|wxTE_READONLY|wxWANTS_CHARS ); m_textCtrlManualActivationUrl->SetMaxLength( 0 ); bSizer237->Add( m_textCtrlManualActivationUrl, 0, wxEXPAND|wxBOTTOM|wxRIGHT|wxLEFT, 5 ); @@ -4457,11 +4457,11 @@ ActivationDlgGenerated::ActivationDlgGenerated( wxWindow* parent, wxWindowID id, m_staticText13611->Wrap( -1 ); bSizer235->Add( m_staticText13611, 0, wxALIGN_CENTER_VERTICAL|wxTOP|wxBOTTOM|wxLEFT, 5 ); - m_textCtrlOfflineActivationKey = new wxTextCtrl( m_panel351, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize( 220,-1 ), wxTE_PROCESS_ENTER|wxWANTS_CHARS ); + m_textCtrlOfflineActivationKey = new wxTextCtrl( m_panel351, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize( 220, -1 ), wxTE_PROCESS_ENTER|wxWANTS_CHARS ); m_textCtrlOfflineActivationKey->SetMaxLength( 0 ); bSizer235->Add( m_textCtrlOfflineActivationKey, 1, wxTOP|wxBOTTOM|wxLEFT|wxALIGN_CENTER_VERTICAL, 5 ); - m_buttonActivateOffline = new wxButton( m_panel351, wxID_ANY, _("Activate offline"), wxDefaultPosition, wxSize( -1,-1 ), 0 ); + m_buttonActivateOffline = new wxButton( m_panel351, wxID_ANY, _("Activate offline"), wxDefaultPosition, wxSize( -1, -1 ), 0 ); m_buttonActivateOffline->SetFont( wxFont( wxNORMAL_FONT->GetPointSize(), 70, 90, 92, false, wxEmptyString ) ); bSizer235->Add( m_buttonActivateOffline, 0, wxALL|wxALIGN_CENTER_VERTICAL, 5 ); @@ -4483,7 +4483,7 @@ ActivationDlgGenerated::ActivationDlgGenerated( wxWindow* parent, wxWindowID id, bSizerStdButtons = new wxBoxSizer( wxHORIZONTAL ); - m_buttonCancel = new wxButton( this, wxID_CANCEL, _("Cancel"), wxDefaultPosition, wxSize( -1,-1 ), 0 ); + m_buttonCancel = new wxButton( this, wxID_CANCEL, _("Cancel"), wxDefaultPosition, wxSize( -1, -1 ), 0 ); bSizerStdButtons->Add( m_buttonCancel, 0, wxALIGN_CENTER_VERTICAL|wxTOP|wxBOTTOM|wxRIGHT, 5 ); diff --git a/FreeFileSync/Source/ui/gui_generated.h b/FreeFileSync/Source/ui/gui_generated.h index ac691409..eab74a04 100644 --- a/FreeFileSync/Source/ui/gui_generated.h +++ b/FreeFileSync/Source/ui/gui_generated.h @@ -252,7 +252,7 @@ public: wxBoxSizer* bSizerStatistics; wxBoxSizer* bSizerData; - MainDialogGenerated( wxWindow* parent, wxWindowID id = wxID_ANY, const wxString& title = _("dummy"), const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxSize( 900,600 ), long style = wxDEFAULT_FRAME_STYLE|wxTAB_TRAVERSAL ); + MainDialogGenerated( wxWindow* parent, wxWindowID id = wxID_ANY, const wxString& title = _("dummy"), const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxSize( 900, 600 ), long style = wxDEFAULT_FRAME_STYLE|wxTAB_TRAVERSAL ); ~MainDialogGenerated(); @@ -443,7 +443,7 @@ protected: public: wxBitmapButton* m_bpButtonSelectAltFolder; - ConfigDlgGenerated( wxWindow* parent, wxWindowID id = wxID_ANY, const wxString& title = _("dummy"), const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxSize( -1,-1 ), long style = wxDEFAULT_DIALOG_STYLE|wxMAXIMIZE_BOX|wxRESIZE_BORDER ); + ConfigDlgGenerated( wxWindow* parent, wxWindowID id = wxID_ANY, const wxString& title = _("dummy"), const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxSize( -1, -1 ), long style = wxDEFAULT_DIALOG_STYLE|wxMAXIMIZE_BOX|wxRESIZE_BORDER ); ~ConfigDlgGenerated(); }; @@ -552,7 +552,7 @@ protected: public: - SftpSetupDlgGenerated( wxWindow* parent, wxWindowID id = wxID_ANY, const wxString& title = _("SSH File Transfer Protocol"), const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxSize( -1,-1 ), long style = wxDEFAULT_DIALOG_STYLE|wxMAXIMIZE_BOX|wxRESIZE_BORDER ); + SftpSetupDlgGenerated( wxWindow* parent, wxWindowID id = wxID_ANY, const wxString& title = _("SSH File Transfer Protocol"), const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxSize( -1, -1 ), long style = wxDEFAULT_DIALOG_STYLE|wxMAXIMIZE_BOX|wxRESIZE_BORDER ); ~SftpSetupDlgGenerated(); }; @@ -584,7 +584,7 @@ protected: public: - SftpFolderPickerGenerated( wxWindow* parent, wxWindowID id = wxID_ANY, const wxString& title = _("SSH File Transfer Protocol"), const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxSize( -1,-1 ), long style = wxDEFAULT_DIALOG_STYLE|wxMAXIMIZE_BOX|wxRESIZE_BORDER ); + SftpFolderPickerGenerated( wxWindow* parent, wxWindowID id = wxID_ANY, const wxString& title = _("SSH File Transfer Protocol"), const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxSize( -1, -1 ), long style = wxDEFAULT_DIALOG_STYLE|wxMAXIMIZE_BOX|wxRESIZE_BORDER ); ~SftpFolderPickerGenerated(); }; @@ -664,7 +664,7 @@ protected: public: zen::Graph2D* m_panelGraphProgress; - CompareProgressDlgGenerated( wxWindow* parent, wxWindowID id = wxID_ANY, const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxSize( -1,-1 ), long style = wxRAISED_BORDER ); + CompareProgressDlgGenerated( wxWindow* parent, wxWindowID id = wxID_ANY, const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxSize( -1, -1 ), long style = wxRAISED_BORDER ); ~CompareProgressDlgGenerated(); }; @@ -712,7 +712,7 @@ public: wxButton* m_buttonPause; wxButton* m_buttonStop; - SyncProgressPanelGenerated( wxWindow* parent, wxWindowID id = wxID_ANY, const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxSize( -1,-1 ), long style = wxTAB_TRAVERSAL ); + SyncProgressPanelGenerated( wxWindow* parent, wxWindowID id = wxID_ANY, const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxSize( -1, -1 ), long style = wxTAB_TRAVERSAL ); ~SyncProgressPanelGenerated(); }; @@ -826,7 +826,7 @@ protected: public: - DeleteDlgGenerated( wxWindow* parent, wxWindowID id = wxID_ANY, const wxString& title = _("Delete Items"), const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxSize( -1,-1 ), long style = wxDEFAULT_DIALOG_STYLE|wxMAXIMIZE_BOX|wxRESIZE_BORDER ); + DeleteDlgGenerated( wxWindow* parent, wxWindowID id = wxID_ANY, const wxString& title = _("Delete Items"), const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxSize( -1, -1 ), long style = wxDEFAULT_DIALOG_STYLE|wxMAXIMIZE_BOX|wxRESIZE_BORDER ); ~DeleteDlgGenerated(); }; @@ -864,7 +864,7 @@ public: FolderHistoryBox* m_targetFolderPath; wxBitmapButton* m_bpButtonSelectAltTargetFolder; - CopyToDlgGenerated( wxWindow* parent, wxWindowID id = wxID_ANY, const wxString& title = _("Copy items"), const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxSize( -1,-1 ), long style = wxDEFAULT_DIALOG_STYLE|wxMAXIMIZE_BOX|wxRESIZE_BORDER ); + CopyToDlgGenerated( wxWindow* parent, wxWindowID id = wxID_ANY, const wxString& title = _("Copy items"), const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxSize( -1, -1 ), long style = wxDEFAULT_DIALOG_STYLE|wxMAXIMIZE_BOX|wxRESIZE_BORDER ); ~CopyToDlgGenerated(); }; diff --git a/FreeFileSync/Source/ui/main_dlg.cpp b/FreeFileSync/Source/ui/main_dlg.cpp index 4d56a4f7..a8891d82 100644 --- a/FreeFileSync/Source/ui/main_dlg.cpp +++ b/FreeFileSync/Source/ui/main_dlg.cpp @@ -323,21 +323,26 @@ void updateTopButton(wxBitmapButton& btn, const wxBitmap& bmp, const wxString& v //################################################################################################################################## -xmlAccess::XmlGlobalSettings loadGlobalConfig(const Zstring& globalConfigFile) //blocks on GUI on errors! +xmlAccess::XmlGlobalSettings loadGlobalConfig(const Zstring& globalConfigFilePath) //blocks on GUI on errors! { using namespace xmlAccess; - XmlGlobalSettings globalCfg; + XmlGlobalSettings globalCfg; try - { - std::wstring warningMsg; - readConfig(globalConfigFile, globalCfg, warningMsg); //throw FileError + { + std::wstring warningMsg; + readConfig(globalConfigFilePath, globalCfg, warningMsg); //throw FileError - assert(warningMsg.empty()); //ignore parsing errors: should be migration problems only *cross-fingers* + assert(warningMsg.empty()); //ignore parsing errors: should be migration problems only *cross-fingers* } catch (const FileError& e) { - showNotificationDialog(nullptr, DialogInfoType::ERROR2, PopupDialogCfg().setDetailInstructions(e.toString())); //no parent window: main dialog not yet created! + bool notExisting = false; + try { notExisting = !getItemTypeIfExists(globalConfigFilePath); /*throw FileError*/ } + catch (FileError&) {} //previous exception is more relevant + + if (!notExisting) //existing or access error + showNotificationDialog(nullptr, DialogInfoType::ERROR2, PopupDialogCfg().setDetailInstructions(e.toString())); //no parent window: main dialog not yet created! } return globalCfg; } @@ -350,13 +355,11 @@ Zstring MainDialog::getLastRunConfigPath() } -void MainDialog::create(const Zstring& globalConfigFile) +void MainDialog::create(const Zstring& globalConfigFilePath) { using namespace xmlAccess; - XmlGlobalSettings globalSettings; - if (fileExists(globalConfigFile)) //else: globalCfg already has default values - globalSettings = loadGlobalConfig(globalConfigFile); + const XmlGlobalSettings globalSettings = loadGlobalConfig(globalConfigFilePath); std::vector<Zstring> cfgFilePaths; for (const ConfigFileItem& item : globalSettings.gui.lastUsedConfigFiles) @@ -364,10 +367,10 @@ void MainDialog::create(const Zstring& globalConfigFile) //------------------------------------------------------------------------------------------ //check existence of all files in parallel: - GetFirstResult<FalseType> firstMissingDir; + GetFirstResult<FalseType> firstUnavailableFile; for (const Zstring& filePath : cfgFilePaths) - firstMissingDir.addJob([filePath] () -> Opt<FalseType> + firstUnavailableFile.addJob([filePath]() -> Opt<FalseType> { assert(!filePath.empty()); if (filePath.empty() /*ever empty??*/ || !fileExists(filePath)) @@ -376,16 +379,16 @@ void MainDialog::create(const Zstring& globalConfigFile) }); //potentially slow network access: give all checks 500ms to finish - const bool allFilesExist = firstMissingDir.timedWait(std::chrono::milliseconds(500)) && //false: time elapsed - !firstMissingDir.get(); //no missing - if (!allFilesExist) + const bool allFilesAvailable = firstUnavailableFile.timedWait(std::chrono::milliseconds(500)) && //false: time elapsed + !firstUnavailableFile.get(); //no missing + if (!allFilesAvailable) cfgFilePaths.clear(); //we do NOT want to show an error due to last config file missing on application start! //------------------------------------------------------------------------------------------ if (cfgFilePaths.empty()) { const Zstring lastRunConfigFilePath = getLastRunConfigPath(); - if (zen::fileExists(lastRunConfigFilePath)) //3. try to load auto-save config + if (fileExists(lastRunConfigFilePath)) //3. try to load auto-save config cfgFilePaths.push_back(lastRunConfigFilePath); } @@ -417,11 +420,11 @@ void MainDialog::create(const Zstring& globalConfigFile) //------------------------------------------------------------------------------------------ - create(globalConfigFile, &globalSettings, guiCfg, cfgFilePaths, false); + create(globalConfigFilePath, &globalSettings, guiCfg, cfgFilePaths, false); } -void MainDialog::create(const Zstring& globalConfigFile, +void MainDialog::create(const Zstring& globalConfigFilePath, const xmlAccess::XmlGlobalSettings* globalSettings, const xmlAccess::XmlGuiConfig& guiCfg, const std::vector<Zstring>& referenceFiles, @@ -430,8 +433,8 @@ void MainDialog::create(const Zstring& globalConfigFile, xmlAccess::XmlGlobalSettings globSett; if (globalSettings) globSett = *globalSettings; - else if (fileExists(globalConfigFile)) - globSett = loadGlobalConfig(globalConfigFile); + else + globSett = loadGlobalConfig(globalConfigFilePath); //else: globalCfg already has default values try @@ -445,7 +448,7 @@ void MainDialog::create(const Zstring& globalConfigFile, //continue! } - MainDialog* frame = new MainDialog(globalConfigFile, guiCfg, referenceFiles, globSett, startComparison); + MainDialog* frame = new MainDialog(globalConfigFilePath, guiCfg, referenceFiles, globSett, startComparison); frame->Show(); #ifdef ZEN_MAC ProcessSerialNumber psn = { 0, kCurrentProcess }; @@ -803,12 +806,16 @@ MainDialog::MainDialog(const Zstring& globalConfigFile, for (const AbstractPath& folderPath : folderPathsToCheck) firstMissingDir.addJob([folderPath]() -> Opt<FalseType> { - if (!AFS::folderExists(folderPath)) - return FalseType(); - return NoValue(); + try + { + if (AFS::getItemType(folderPath) != AFS::ItemType::FILE) //throw FileError + return NoValue(); + } + catch (FileError&) {} + return FalseType(); }); - const bool startComparisonNow = !firstMissingDir.timedWait(std::chrono::milliseconds(500)) || //= no result yet => start comparison anyway! + const bool startComparisonNow = !firstMissingDir.timedWait(std::chrono::milliseconds(500)) || //= no result yet => start comparison anyway! !firstMissingDir.get(); //= all directories exist if (startComparisonNow) @@ -2794,7 +2801,7 @@ void MainDialog::removeObsoleteCfgHistoryItems(const std::vector<Zstring>& fileP std::list<std::future<bool>> fileEx; for (const Zstring& filePath : filePaths) - fileEx.push_back(zen::runAsync([=] { return somethingExists(filePath); })); + fileEx.push_back(zen::runAsync([=] { return fileExists(filePath); })); //potentially slow network access => limit maximum wait time! wait_for_all_timed(fileEx.begin(), fileEx.end(), std::chrono::milliseconds(1000)); @@ -3118,8 +3125,8 @@ void MainDialog::OnConfigLoad(wxCommandEvent& event) wxFileDialog filePicker(this, wxString(), - utfCvrtTo<wxString>(beforeLast(activeCfgFilename, FILE_NAME_SEPARATOR, IF_MISSING_RETURN_NONE)), //set default dir - wxString(), + utfCvrtTo<wxString>(beforeLast(activeCfgFilename, FILE_NAME_SEPARATOR, IF_MISSING_RETURN_NONE)), //default dir + wxString(), //default file wxString(L"FreeFileSync (*.ffs_gui; *.ffs_batch)|*.ffs_gui;*.ffs_batch") + L"|" +_("All files") + L" (*.*)|*", wxFD_OPEN | wxFD_MULTIPLE); if (filePicker.ShowModal() == wxID_OK) @@ -3660,13 +3667,13 @@ void MainDialog::setViewFilterDefault() setButton(m_bpButtonShowRightNewer, def.rightNewer); setButton(m_bpButtonShowDifferent, def.different); - setButton(m_bpButtonShowCreateLeft, def.createLeft); - setButton(m_bpButtonShowCreateRight,def.createRight); - setButton(m_bpButtonShowUpdateLeft, def.updateLeft); - setButton(m_bpButtonShowUpdateRight,def.updateRight); - setButton(m_bpButtonShowDeleteLeft, def.deleteLeft); - setButton(m_bpButtonShowDeleteRight,def.deleteRight); - setButton(m_bpButtonShowDoNothing, def.doNothing); + setButton(m_bpButtonShowCreateLeft, def.createLeft); + setButton(m_bpButtonShowCreateRight, def.createRight); + setButton(m_bpButtonShowUpdateLeft, def.updateLeft); + setButton(m_bpButtonShowUpdateRight, def.updateRight); + setButton(m_bpButtonShowDeleteLeft, def.deleteLeft); + setButton(m_bpButtonShowDeleteRight, def.deleteRight); + setButton(m_bpButtonShowDoNothing, def.doNothing); } @@ -3788,7 +3795,7 @@ void MainDialog::OnCompare(wxCommandEvent& event) if (!globalCfg.soundFileCompareFinished.empty()) { const Zstring soundFile = getResourceDirPf() + globalCfg.soundFileCompareFinished; - if (fileExists(soundFile)) + if (fileAvailable(soundFile)) wxSound::Play(utfCvrtTo<wxString>(soundFile), wxSOUND_ASYNC); //warning: this may fail and show a wxWidgets error message! => must not play when running FFS as batch! } @@ -3959,18 +3966,18 @@ void MainDialog::OnStartSync(wxCommandEvent& event) std::unique_ptr<LockHolder> dirLocks; if (globalCfg.createLockFile) { - std::set<Zstring, LessFilePath> dirPathsExisting; + std::set<Zstring, LessFilePath> availableDirPaths; for (auto it = begin(folderCmp); it != end(folderCmp); ++it) { - if (it->isExisting<LEFT_SIDE>()) //do NOT check directory existence again! + if (it->isAvailable<LEFT_SIDE>()) //do NOT check directory existence again! if (Opt<Zstring> nativeFolderPath = AFS::getNativeItemPath(it->getAbstractPath<LEFT_SIDE>())) //restrict directory locking to native paths until further - dirPathsExisting.insert(*nativeFolderPath); + availableDirPaths.insert(*nativeFolderPath); - if (it->isExisting<RIGHT_SIDE>()) + if (it->isAvailable<RIGHT_SIDE>()) if (Opt<Zstring> nativeFolderPath = AFS::getNativeItemPath(it->getAbstractPath<RIGHT_SIDE>())) - dirPathsExisting.insert(*nativeFolderPath); + availableDirPaths.insert(*nativeFolderPath); } - dirLocks = std::make_unique<LockHolder>(dirPathsExisting, globalCfg.optDialogs.warningDirectoryLockFailed, statusHandler); + dirLocks = std::make_unique<LockHolder>(availableDirPaths, globalCfg.optDialogs.warningDirectoryLockFailed, statusHandler); } //START SYNCHRONIZATION @@ -4146,23 +4153,23 @@ void MainDialog::updateGridViewData() filesizeRightView = result.filesizeRightView; //sync preview buttons - updateVisibility(m_bpButtonShowExcluded , result.existsExcluded); - updateVisibility(m_bpButtonShowEqual , result.existsEqual); - updateVisibility(m_bpButtonShowConflict , result.existsConflict); + updateVisibility(m_bpButtonShowExcluded, result.existsExcluded); + updateVisibility(m_bpButtonShowEqual, result.existsEqual); + updateVisibility(m_bpButtonShowConflict, result.existsConflict); - updateVisibility(m_bpButtonShowCreateLeft , result.existsSyncCreateLeft); + updateVisibility(m_bpButtonShowCreateLeft, result.existsSyncCreateLeft); updateVisibility(m_bpButtonShowCreateRight, result.existsSyncCreateRight); - updateVisibility(m_bpButtonShowDeleteLeft , result.existsSyncDeleteLeft); + updateVisibility(m_bpButtonShowDeleteLeft, result.existsSyncDeleteLeft); updateVisibility(m_bpButtonShowDeleteRight, result.existsSyncDeleteRight); - updateVisibility(m_bpButtonShowUpdateLeft , result.existsSyncDirLeft); + updateVisibility(m_bpButtonShowUpdateLeft, result.existsSyncDirLeft); updateVisibility(m_bpButtonShowUpdateRight, result.existsSyncDirRight); - updateVisibility(m_bpButtonShowDoNothing , result.existsSyncDirNone); + updateVisibility(m_bpButtonShowDoNothing, result.existsSyncDirNone); - updateVisibility(m_bpButtonShowLeftOnly , false); - updateVisibility(m_bpButtonShowRightOnly , false); - updateVisibility(m_bpButtonShowLeftNewer , false); + updateVisibility(m_bpButtonShowLeftOnly, false); + updateVisibility(m_bpButtonShowRightOnly, false); + updateVisibility(m_bpButtonShowLeftNewer, false); updateVisibility(m_bpButtonShowRightNewer, false); - updateVisibility(m_bpButtonShowDifferent , false); + updateVisibility(m_bpButtonShowDifferent, false); } else { @@ -4182,23 +4189,23 @@ void MainDialog::updateGridViewData() filesizeRightView = result.filesizeRightView; //comparison result view buttons - updateVisibility(m_bpButtonShowExcluded , result.existsExcluded); - updateVisibility(m_bpButtonShowEqual , result.existsEqual); - updateVisibility(m_bpButtonShowConflict , result.existsConflict); + updateVisibility(m_bpButtonShowExcluded, result.existsExcluded); + updateVisibility(m_bpButtonShowEqual, result.existsEqual); + updateVisibility(m_bpButtonShowConflict, result.existsConflict); - updateVisibility(m_bpButtonShowCreateLeft , false); + updateVisibility(m_bpButtonShowCreateLeft, false); updateVisibility(m_bpButtonShowCreateRight, false); - updateVisibility(m_bpButtonShowDeleteLeft , false); + updateVisibility(m_bpButtonShowDeleteLeft, false); updateVisibility(m_bpButtonShowDeleteRight, false); - updateVisibility(m_bpButtonShowUpdateLeft , false); + updateVisibility(m_bpButtonShowUpdateLeft, false); updateVisibility(m_bpButtonShowUpdateRight, false); - updateVisibility(m_bpButtonShowDoNothing , false); + updateVisibility(m_bpButtonShowDoNothing, false); - updateVisibility(m_bpButtonShowLeftOnly , result.existsLeftOnly); - updateVisibility(m_bpButtonShowRightOnly , result.existsRightOnly); - updateVisibility(m_bpButtonShowLeftNewer , result.existsLeftNewer); + updateVisibility(m_bpButtonShowLeftOnly, result.existsLeftOnly); + updateVisibility(m_bpButtonShowRightOnly, result.existsRightOnly); + updateVisibility(m_bpButtonShowLeftNewer, result.existsLeftNewer); updateVisibility(m_bpButtonShowRightNewer, result.existsRightNewer); - updateVisibility(m_bpButtonShowDifferent , result.existsDifferent); + updateVisibility(m_bpButtonShowDifferent, result.existsDifferent); } const bool anySelectViewButtonShown = m_bpButtonShowEqual ->IsShown() || @@ -4497,7 +4504,7 @@ void MainDialog::OnShowFolderPairOptions(wxEvent& event) ContextMenu menu; menu.addItem(_("Add folder pair"), [this, pos] { insertAddFolderPair({ FolderPairEnh() }, pos); }, &getResourceImage(L"item_add_small")); menu.addSeparator(); - menu.addItem(_("Move up" ) + L"\tAlt+Page Up" , [this, pos] { moveAddFolderPairUp(pos); }, &getResourceImage(L"move_up_small")); + menu.addItem(_("Move up" ) + L"\tAlt+Page Up", [this, pos] { moveAddFolderPairUp(pos); }, &getResourceImage(L"move_up_small")); menu.addItem(_("Move down") + L"\tAlt+Page Down", [this, pos] { moveAddFolderPairUp(pos + 1); }, &getResourceImage(L"move_down_small"), pos + 1 < makeSigned(additionalFolderPairs.size())); wxPoint ctxPos = (*it)->m_bpButtonFolderPairOptions->GetPosition(); @@ -4801,9 +4808,9 @@ void MainDialog::OnMenuExportFileList(wxCommandEvent& event) auto colAttrCenter = m_gridMainC->getColumnConfig(); auto colAttrRight = m_gridMainR->getColumnConfig(); - erase_if(colAttrLeft , [](const Grid::ColumnAttribute& ca) { return !ca.visible_; }); + erase_if(colAttrLeft, [](const Grid::ColumnAttribute& ca) { return !ca.visible_; }); erase_if(colAttrCenter, [](const Grid::ColumnAttribute& ca) { return !ca.visible_ || static_cast<ColumnTypeCenter>(ca.type_) == ColumnTypeCenter::CHECKBOX; }); - erase_if(colAttrRight , [](const Grid::ColumnAttribute& ca) { return !ca.visible_; }); + erase_if(colAttrRight, [](const Grid::ColumnAttribute& ca) { return !ca.visible_; }); if (provLeft && provCenter && provRight) { diff --git a/FreeFileSync/Source/ui/main_dlg.h b/FreeFileSync/Source/ui/main_dlg.h index 0aace163..43727211 100644 --- a/FreeFileSync/Source/ui/main_dlg.h +++ b/FreeFileSync/Source/ui/main_dlg.h @@ -217,8 +217,8 @@ private: void OnClose (wxCloseEvent& event) override; void OnCmpSettings (wxCommandEvent& event) override { showConfigDialog(zen::SyncConfigPanel::COMPARISON, -1); } - void OnConfigureFilter(wxCommandEvent& event) override { showConfigDialog(zen::SyncConfigPanel::FILTER , -1); } - void OnSyncSettings (wxCommandEvent& event) override { showConfigDialog(zen::SyncConfigPanel::SYNC , -1); } + void OnConfigureFilter(wxCommandEvent& event) override { showConfigDialog(zen::SyncConfigPanel::FILTER, -1); } + void OnSyncSettings (wxCommandEvent& event) override { showConfigDialog(zen::SyncConfigPanel::SYNC, -1); } void showConfigDialog(zen::SyncConfigPanel panelToShow, int localPairIndexToShow); diff --git a/FreeFileSync/Source/ui/progress_indicator.cpp b/FreeFileSync/Source/ui/progress_indicator.cpp index 93e721ea..d87b5343 100644 --- a/FreeFileSync/Source/ui/progress_indicator.cpp +++ b/FreeFileSync/Source/ui/progress_indicator.cpp @@ -2050,7 +2050,7 @@ void SyncProgressDialogImpl<TopLevelDialog>::processHasFinished(SyncResult resul if (!soundFileSyncComplete_.empty()) { const Zstring soundFile = getResourceDirPf() + soundFileSyncComplete_; - if (fileExists(soundFile)) + if (fileAvailable(soundFile)) wxSound::Play(utfCvrtTo<wxString>(soundFile), wxSOUND_ASYNC); //warning: this may fail and show a wxWidgets error message! => must not play when running FFS as batch! } break; diff --git a/FreeFileSync/Source/ui/small_dlgs.cpp b/FreeFileSync/Source/ui/small_dlgs.cpp index fdc28196..175aaf31 100644 --- a/FreeFileSync/Source/ui/small_dlgs.cpp +++ b/FreeFileSync/Source/ui/small_dlgs.cpp @@ -209,7 +209,7 @@ private: void OnAuthPassword(wxCommandEvent& event) override { autType_ = AuthType::PASSWORD; updateGui(); } void OnAuthKeyfile (wxCommandEvent& event) override { autType_ = AuthType::KEYFILE; updateGui(); } - void OnSelectKeyfile (wxCommandEvent& event) override; + void OnSelectKeyfile(wxCommandEvent& event) override; void updateGui(); @@ -327,8 +327,8 @@ void SftpSetupDlg::OnSelectKeyfile (wxCommandEvent& event) { wxFileDialog filePicker(this, wxString(), - beforeLast(m_textCtrlKeyfilePath->GetValue(), utfCvrtTo<wxString>(FILE_NAME_SEPARATOR), IF_MISSING_RETURN_NONE), //set default dir - wxString(), + beforeLast(m_textCtrlKeyfilePath->GetValue(), utfCvrtTo<wxString>(FILE_NAME_SEPARATOR), IF_MISSING_RETURN_NONE), //default dir + wxString(), //default file wxString(L"OpenSSL PEM (*.pem)|*.pem") + L"|" +_("All files") + L" (*.*)|*", wxFD_OPEN); if (filePicker.ShowModal() == wxID_OK) diff --git a/FreeFileSync/Source/ui/sorting.h b/FreeFileSync/Source/ui/sorting.h index fe842745..b5deb2bc 100644 --- a/FreeFileSync/Source/ui/sorting.h +++ b/FreeFileSync/Source/ui/sorting.h @@ -81,7 +81,7 @@ bool lessRelativeFolder(const FileSystemObject& a, const FileSystemObject& b) const bool isDirectoryB = isDirectoryPair(b); const Zstring& relFolderB = isDirectoryB ? b.getPairRelativePath() : //directory - beforeLast(b.getPairRelativePath(), FILE_NAME_SEPARATOR ,IF_MISSING_RETURN_NONE); + beforeLast(b.getPairRelativePath(), FILE_NAME_SEPARATOR, IF_MISSING_RETURN_NONE); //compare relative names without filepaths first const int rv = cmpFilePath(relFolderA.c_str(), relFolderA.size(), diff --git a/FreeFileSync/Source/ui/sync_cfg.cpp b/FreeFileSync/Source/ui/sync_cfg.cpp index 9bac8df3..4020c4cc 100644 --- a/FreeFileSync/Source/ui/sync_cfg.cpp +++ b/FreeFileSync/Source/ui/sync_cfg.cpp @@ -68,7 +68,7 @@ private: }; //------------- comparison panel ---------------------- - void OnHelpComparisonSettings(wxHyperlinkEvent& event) override { displayHelpEntry(L"comparison-settings" , this); } + void OnHelpComparisonSettings(wxHyperlinkEvent& event) override { displayHelpEntry(L"comparison-settings", this); } void OnHelpTimeShift (wxHyperlinkEvent& event) override { displayHelpEntry(L"daylight-saving-time", this); } void OnToggleLocalCompSettings(wxCommandEvent& event) override { updateCompGui(); updateSyncGui(); /*affects sync settings, too!*/ } @@ -131,8 +131,8 @@ private: void OnToggleDeletionType(wxCommandEvent& event) override { toggleDeletionPolicy(handleDeletion_); updateSyncGui(); } - void OnHelpDetectMovedFiles(wxHyperlinkEvent& event) override { displayHelpEntry(L"synchronization-settings" , this); } - void OnHelpVersioning (wxHyperlinkEvent& event) override { displayHelpEntry(L"versioning", this); } + void OnHelpDetectMovedFiles(wxHyperlinkEvent& event) override { displayHelpEntry(L"synchronization-settings", this); } + void OnHelpVersioning (wxHyperlinkEvent& event) override { displayHelpEntry(L"versioning", this); } std::shared_ptr<const SyncConfig> getSyncConfig() const; void setSyncConfig(std::shared_ptr<const SyncConfig> syncCfg); @@ -829,11 +829,11 @@ void updateSyncDirectionIcons(const DirectionConfig& directionCfg, const DirectionSet dirCfg = extractDirections(directionCfg); - updateButton(buttonLeftOnly , dirCfg.exLeftSideOnly , L"so_delete_left", L"so_none", L"so_create_right", SO_DELETE_LEFT , SO_DO_NOTHING, SO_CREATE_NEW_RIGHT); - updateButton(buttonRightOnly , dirCfg.exRightSideOnly, L"so_create_left", L"so_none", L"so_delete_right", SO_CREATE_NEW_LEFT, SO_DO_NOTHING, SO_DELETE_RIGHT ); - updateButton(buttonLeftNewer , dirCfg.leftNewer , L"so_update_left", L"so_none", L"so_update_right", SO_OVERWRITE_LEFT , SO_DO_NOTHING, SO_OVERWRITE_RIGHT ); - updateButton(buttonRightNewer, dirCfg.rightNewer , L"so_update_left", L"so_none", L"so_update_right", SO_OVERWRITE_LEFT , SO_DO_NOTHING, SO_OVERWRITE_RIGHT ); - updateButton(buttonDifferent , dirCfg.different , L"so_update_left", L"so_none", L"so_update_right", SO_OVERWRITE_LEFT , SO_DO_NOTHING, SO_OVERWRITE_RIGHT ); + updateButton(buttonLeftOnly, dirCfg.exLeftSideOnly, L"so_delete_left", L"so_none", L"so_create_right", SO_DELETE_LEFT, SO_DO_NOTHING, SO_CREATE_NEW_RIGHT); + updateButton(buttonRightOnly, dirCfg.exRightSideOnly, L"so_create_left", L"so_none", L"so_delete_right", SO_CREATE_NEW_LEFT, SO_DO_NOTHING, SO_DELETE_RIGHT ); + updateButton(buttonLeftNewer, dirCfg.leftNewer, L"so_update_left", L"so_none", L"so_update_right", SO_OVERWRITE_LEFT, SO_DO_NOTHING, SO_OVERWRITE_RIGHT ); + updateButton(buttonRightNewer, dirCfg.rightNewer, L"so_update_left", L"so_none", L"so_update_right", SO_OVERWRITE_LEFT, SO_DO_NOTHING, SO_OVERWRITE_RIGHT ); + updateButton(buttonDifferent, dirCfg.different, L"so_update_left", L"so_none", L"so_update_right", SO_OVERWRITE_LEFT, SO_DO_NOTHING, SO_OVERWRITE_RIGHT ); switch (dirCfg.conflict) { diff --git a/FreeFileSync/Source/ui/tree_view.cpp b/FreeFileSync/Source/ui/tree_view.cpp index cbfb5305..0db294f1 100644 --- a/FreeFileSync/Source/ui/tree_view.cpp +++ b/FreeFileSync/Source/ui/tree_view.cpp @@ -573,13 +573,13 @@ void TreeView::updateCmpResult(bool showExcluded, bool conflictFilesActive) { updateView([showExcluded, //make sure the predicate can be stored safely! - leftOnlyFilesActive, - rightOnlyFilesActive, - leftNewerFilesActive, - rightNewerFilesActive, - differentFilesActive, - equalFilesActive, - conflictFilesActive](const FileSystemObject& fsObj) -> bool + leftOnlyFilesActive, + rightOnlyFilesActive, + leftNewerFilesActive, + rightNewerFilesActive, + differentFilesActive, + equalFilesActive, + conflictFilesActive](const FileSystemObject& fsObj) -> bool { if (!fsObj.isActive() && !showExcluded) return false; @@ -620,15 +620,15 @@ void TreeView::updateSyncPreview(bool showExcluded, bool conflictFilesActive) { updateView([showExcluded, //make sure the predicate can be stored safely! - syncCreateLeftActive, - syncCreateRightActive, - syncDeleteLeftActive, - syncDeleteRightActive, - syncDirOverwLeftActive, - syncDirOverwRightActive, - syncDirNoneActive, - syncEqualActive, - conflictFilesActive](const FileSystemObject& fsObj) -> bool + syncCreateLeftActive, + syncCreateRightActive, + syncDeleteLeftActive, + syncDeleteRightActive, + syncDirOverwLeftActive, + syncDirOverwRightActive, + syncDirNoneActive, + syncEqualActive, + conflictFilesActive](const FileSystemObject& fsObj) -> bool { if (!fsObj.isActive() && !showExcluded) return false; diff --git a/FreeFileSync/Source/ui/version_check.cpp b/FreeFileSync/Source/ui/version_check.cpp index da7b0e08..8783130c 100644 --- a/FreeFileSync/Source/ui/version_check.cpp +++ b/FreeFileSync/Source/ui/version_check.cpp @@ -143,7 +143,7 @@ std::vector<std::pair<std::string, std::string>> geHttpPostParameters() #endif params.emplace_back("language", utfCvrtTo<std::string>(getIso639Language())); - params.emplace_back("country" , utfCvrtTo<std::string>(getIso3166Country())); + params.emplace_back("country", utfCvrtTo<std::string>(getIso3166Country())); return params; } @@ -174,10 +174,10 @@ void runFreeFileSyncAutoUpdate(wxWindow* parent, const std::wstring& directDownl const Zstring installerLocalPath = dirPath.empty() ? fileName : appendSeparator(dirPath) + fileName; const Zstring installerLocalPathTmp = installerLocalPath + L".tmp"; - - if (!fileExists(installerLocalPath)) + +if (!getItemTypeIfExists(installerLocalPath)) //throw FileError { - ZEN_ON_SCOPE_FAIL(try { removeFile(installerLocalPathTmp); } + ZEN_ON_SCOPE_FAIL(try { removeFilePlain(installerLocalPathTmp); } catch (FileError&) {}); { @@ -297,7 +297,7 @@ void showUpdateAvailableDialog(wxWindow* parent, const std::string& onlineVersio } catch (const FileError& e) //fall back to regular update info dialog: { - updateDetailsMsg = e.toString() + L"\n\n" + updateDetailsMsg; + updateDetailsMsg = e.toString() + L"\n\n\n" + updateDetailsMsg; } switch (showConfirmationDialog(parent, DialogInfoType::INFO, PopupDialogCfg(). diff --git a/FreeFileSync/Source/ui/version_check_impl.h b/FreeFileSync/Source/ui/version_check_impl.h index 58412517..e408d7a1 100644 --- a/FreeFileSync/Source/ui/version_check_impl.h +++ b/FreeFileSync/Source/ui/version_check_impl.h @@ -7,6 +7,8 @@ #ifndef VERSION_ID_HEADER_2348769284769242 #define VERSION_ID_HEADER_2348769284769242 +#include <ctime> +#include <zen/basic_math.h> #include "../version/version.h" @@ -38,14 +40,14 @@ time_t getVersionCheckInactiveId() inline time_t getVersionCheckCurrentTime() { - return std::time(nullptr); + return std::time(nullptr); } bool shouldRunPeriodicUpdateCheck(time_t lastUpdateCheck) { - if (lastUpdateCheck == getVersionCheckInactiveId()) - return false; + if (lastUpdateCheck == getVersionCheckInactiveId()) + return false; const time_t now = std::time(nullptr); return numeric::dist(now, lastUpdateCheck) >= 7 * 24 * 3600; //check weekly diff --git a/FreeFileSync/Source/version/version.h b/FreeFileSync/Source/version/version.h index f34673b8..dce88009 100644 --- a/FreeFileSync/Source/version/version.h +++ b/FreeFileSync/Source/version/version.h @@ -3,7 +3,7 @@ namespace zen { -const char ffsVersion[] = "8.7"; //internal linkage! +const char ffsVersion[] = "8.8"; //internal linkage! const char FFS_VERSION_SEPARATOR = '.'; } diff --git a/wx+/graph.cpp b/wx+/graph.cpp index 192e5513..d45a0e85 100644 --- a/wx+/graph.cpp +++ b/wx+/graph.cpp @@ -91,7 +91,7 @@ public: int realToScreenRound(double realPos) const //returns -1 and screenSize + 1 if out of bounds! { //catch large double values: if double is larger than what int can represent => undefined behavior! - numeric::clamp(realPos , outOfBoundsLow, outOfBoundsHigh); + numeric::clamp(realPos, outOfBoundsLow, outOfBoundsHigh); return numeric::round(realToScreen(realPos)); } diff --git a/wx+/popup_dlg_generated.cpp b/wx+/popup_dlg_generated.cpp index 6df18dce..a79ffa52 100644 --- a/wx+/popup_dlg_generated.cpp +++ b/wx+/popup_dlg_generated.cpp @@ -11,7 +11,7 @@ PopupDialogGenerated::PopupDialogGenerated( wxWindow* parent, wxWindowID id, const wxString& title, const wxPoint& pos, const wxSize& size, long style ) : wxDialog( parent, id, title, pos, size, style ) { - this->SetSizeHints( wxSize( -1,-1 ), wxDefaultSize ); + this->SetSizeHints( wxSize( -1, -1 ), wxDefaultSize ); this->SetBackgroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_BTNFACE ) ); wxBoxSizer* bSizer24; @@ -23,7 +23,7 @@ PopupDialogGenerated::PopupDialogGenerated( wxWindow* parent, wxWindowID id, con wxBoxSizer* bSizer165; bSizer165 = new wxBoxSizer( wxHORIZONTAL ); - m_bitmapMsgType = new wxStaticBitmap( m_panel33, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( -1,-1 ), 0 ); + m_bitmapMsgType = new wxStaticBitmap( m_panel33, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( -1, -1 ), 0 ); bSizer165->Add( m_bitmapMsgType, 0, wxALL, 10 ); wxBoxSizer* bSizer16; @@ -62,13 +62,13 @@ PopupDialogGenerated::PopupDialogGenerated( wxWindow* parent, wxWindowID id, con bSizerStdButtons = new wxBoxSizer( wxHORIZONTAL ); - m_buttonAffirmative = new wxButton( this, wxID_YES, _("dummy"), wxDefaultPosition, wxSize( -1,-1 ), 0 ); + m_buttonAffirmative = new wxButton( this, wxID_YES, _("dummy"), wxDefaultPosition, wxSize( -1, -1 ), 0 ); bSizerStdButtons->Add( m_buttonAffirmative, 0, wxALIGN_CENTER_VERTICAL|wxBOTTOM|wxRIGHT|wxLEFT, 5 ); - m_buttonNegative = new wxButton( this, wxID_NO, _("dummy"), wxDefaultPosition, wxSize( -1,-1 ), 0 ); + m_buttonNegative = new wxButton( this, wxID_NO, _("dummy"), wxDefaultPosition, wxSize( -1, -1 ), 0 ); bSizerStdButtons->Add( m_buttonNegative, 0, wxALIGN_CENTER_VERTICAL|wxBOTTOM|wxRIGHT, 5 ); - m_buttonCancel = new wxButton( this, wxID_CANCEL, _("Cancel"), wxDefaultPosition, wxSize( -1,-1 ), 0 ); + m_buttonCancel = new wxButton( this, wxID_CANCEL, _("Cancel"), wxDefaultPosition, wxSize( -1, -1 ), 0 ); bSizerStdButtons->Add( m_buttonCancel, 0, wxALIGN_CENTER_VERTICAL|wxBOTTOM|wxRIGHT, 5 ); diff --git a/zen/dir_watcher.cpp b/zen/dir_watcher.cpp index 98190bba..79e8aeb7 100644 --- a/zen/dir_watcher.cpp +++ b/zen/dir_watcher.cpp @@ -396,7 +396,7 @@ DirWatcher::DirWatcher(const Zstring& dirPath) : //throw FileError traverse = [&traverse, &fullFolderList](const Zstring& path) { traverseFolder(path, nullptr, - [&](const DirInfo& di ) { fullFolderList.push_back(di.fullPath); traverse(di.fullPath); }, + [&](const FolderInfo& fi ) { fullFolderList.push_back(fi.fullPath); traverse(fi.fullPath); }, nullptr, //don't traverse into symlinks (analog to windows build) [&](const std::wstring& errorMsg) { throw FileError(errorMsg); }); }; diff --git a/zen/file_access.cpp b/zen/file_access.cpp index bad1b60d..ac68330e 100644 --- a/zen/file_access.cpp +++ b/zen/file_access.cpp @@ -44,7 +44,140 @@ using namespace zen; -bool zen::fileExists(const Zstring& filePath) +Opt<Zstring> zen::getParentFolderPath(const Zstring& itemPath) +{ +#ifdef ZEN_WIN + assert(startsWith(itemPath, L"\\\\") || //we do NOT support relative paths! + (itemPath.size() >= 3 && isAlpha(itemPath[0]) && itemPath[1] == L':' && itemPath[2] == L'\\')); + + //remove trailing separator (even for C:\ root directories) + const Zstring itemPathFmt = endsWith(itemPath, FILE_NAME_SEPARATOR) ? + beforeLast(itemPath, FILE_NAME_SEPARATOR, IF_MISSING_RETURN_NONE) : + itemPath; + + if (startsWith(itemPathFmt, L"\\\\")) //UNC-name, e.g. \\server-name\share + if (std::count(itemPathFmt.begin(), itemPathFmt.end(), FILE_NAME_SEPARATOR) <= 3) + return NoValue(); + + const Zstring parentDir = beforeLast(itemPathFmt, FILE_NAME_SEPARATOR, IF_MISSING_RETURN_NONE); + if (parentDir.empty()) + return NoValue(); + if (parentDir.size() == 2 && isAlpha(parentDir[0]) && parentDir[1] == L':') + return appendSeparator(parentDir); + +#elif defined ZEN_LINUX || defined ZEN_MAC + assert(startsWith(itemPath, L"/")); //we do NOT support relative paths! + + if (itemPath == "/") + return NoValue(); + + const Zstring parentDir = beforeLast(itemPath, FILE_NAME_SEPARATOR, IF_MISSING_RETURN_NONE); + if (parentDir.empty()) + return Zstring("/"); +#endif + return parentDir; +} + + +ItemType zen::getItemType(const Zstring& itemPath) //throw FileError +{ +#ifdef ZEN_WIN + const DWORD attr = ::GetFileAttributes(applyLongPathPrefix(itemPath).c_str()); + if (attr == INVALID_FILE_ATTRIBUTES) + { + const DWORD ec = ::GetLastError(); //copy before directly/indirectly making other system calls! + if (ec == ERROR_PATH_NOT_FOUND || // + ec == ERROR_FILE_NOT_FOUND || //perf: short circuit for common "not existing" error codes + ec == ERROR_BAD_NETPATH || // + ec == ERROR_BAD_NET_NAME) // + THROW_LAST_FILE_ERROR(replaceCpy(_("Cannot read file attributes of %x."), L"%x", fmtPath(itemPath)), L"GetFileAttributes"); + } + + if (attr != INVALID_FILE_ATTRIBUTES && (attr & FILE_ATTRIBUTE_REPARSE_POINT) == 0) + return (attr & FILE_ATTRIBUTE_DIRECTORY) != 0 ? ItemType::FOLDER : ItemType::FILE; + + //handle obscure file permission problem where ::GetFileAttributes() fails with ERROR_ACCESS_DENIED or ERROR_SHARING_VIOLATION + //while parent directory traversal is successful: e.g. "C:\pagefile.sys" + WIN32_FIND_DATA itemInfo = {}; + const HANDLE searchHandle = ::FindFirstFile(applyLongPathPrefix(itemPath).c_str(), &itemInfo); + if (searchHandle == INVALID_HANDLE_VALUE) + THROW_LAST_FILE_ERROR(replaceCpy(_("Cannot read file attributes of %x."), L"%x", fmtPath(itemPath)), L"FindFirstFile"); + ::FindClose(searchHandle); + + if (isSymlink(itemInfo)) //not every FILE_ATTRIBUTE_REPARSE_POINT is a symlink!!! + return ItemType::SYMLINK; + return (itemInfo.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0 ? ItemType::FOLDER : ItemType::FILE; + +#elif defined ZEN_LINUX || defined ZEN_MAC + struct ::stat itemInfo = {}; + if (::lstat(itemPath.c_str(), &itemInfo) != 0) + THROW_LAST_FILE_ERROR(replaceCpy(_("Cannot read file attributes of %x."), L"%x", fmtPath(itemPath)), L"lstat"); + + if (S_ISLNK(itemInfo.st_mode)) + return ItemType::SYMLINK; + if (S_ISDIR(itemInfo.st_mode)) + return ItemType::FOLDER; + return ItemType::FILE; //S_ISREG || S_ISCHR || S_ISBLK || S_ISFIFO || S_ISSOCK +#endif +} + + +PathDetails zen::getPathDetails(const Zstring& itemPath) //throw FileError +{ + const Opt<Zstring> parentPath = getParentFolderPath(itemPath); + try + { + return { getItemType(itemPath), itemPath, {} }; //throw FileError + } + catch (FileError&) + { + if (!parentPath) //device root + throw; + //else: let's dig deeper... don't bother checking Win32 codes; e.g. not existing item may have the codes: + // ERROR_FILE_NOT_FOUND, ERROR_PATH_NOT_FOUND, ERROR_INVALID_NAME, ERROR_INVALID_DRIVE, + // ERROR_NOT_READY, ERROR_INVALID_PARAMETER, ERROR_BAD_PATHNAME, ERROR_BAD_NETPATH => not reliable + } + const Zstring itemName = afterLast(itemPath, FILE_NAME_SEPARATOR, IF_MISSING_RETURN_ALL); + assert(!itemName.empty()); + + PathDetails pd = getPathDetails(*parentPath); //throw FileError + if (!pd.relPath.empty()) + { + pd.relPath.push_back(itemName); + return { pd.existingType, pd.existingPath, pd.relPath }; + } + + try + { + traverseFolder(*parentPath, + [&](const FileInfo& fi) { if (equalFilePath(fi.itemName, itemName)) throw ItemType::FILE; }, + [&](const FolderInfo& fi) { if (equalFilePath(fi.itemName, itemName)) throw ItemType::FOLDER; }, + [&](const SymlinkInfo& si) { if (equalFilePath(si.itemName, itemName)) throw ItemType::SYMLINK; }, + [](const std::wstring& errorMsg) { throw FileError(errorMsg); }); + + return { pd.existingType, *parentPath, { itemName } }; //throw FileError + } + catch (const ItemType& type) { return { type, itemPath, {} }; } //yes, exceptions for control-flow are bad design... but, but... + //we're not CPU-bound here and finding the item after getItemType() previously failed is exceptional (even C:\pagefile.sys should be found) +} + + +Opt<ItemType> zen::getItemTypeIfExists(const Zstring& itemPath) //throw FileError +{ + const PathDetails pd = getPathDetails(itemPath); //throw FileError +#ifndef NDEBUG + Zstring reconstructedPath = pd.existingPath; + for (const Zstring& itemName : pd.relPath) + reconstructedPath += endsWith(reconstructedPath, FILE_NAME_SEPARATOR) ? itemName : FILE_NAME_SEPARATOR + itemName; + assert(itemPath == reconstructedPath); +#endif + if (pd.relPath.empty()) + return pd.existingType; + return NoValue(); +} + + +bool zen::fileAvailable(const Zstring& filePath) //noexcept { //symbolic links (broken or not) are also treated as existing files! #ifdef ZEN_WIN @@ -61,13 +194,13 @@ bool zen::fileExists(const Zstring& filePath) } -bool zen::dirExists(const Zstring& dirPath) +bool zen::dirAvailable(const Zstring& dirPath) //noexcept { //symbolic links (broken or not) are also treated as existing directories! #ifdef ZEN_WIN const DWORD attr = ::GetFileAttributes(applyLongPathPrefix(dirPath).c_str()); if (attr != INVALID_FILE_ATTRIBUTES) - return (attr & FILE_ATTRIBUTE_DIRECTORY) != 0; //returns true for (dir-)symlinks also + return (attr & FILE_ATTRIBUTE_DIRECTORY) != 0; //returns true for ((broken) dir-)symlinks also #elif defined ZEN_LINUX || defined ZEN_MAC struct ::stat dirInfo = {}; @@ -78,54 +211,39 @@ bool zen::dirExists(const Zstring& dirPath) } -bool zen::symlinkExists(const Zstring& linkPath) +warn_static("remove following test functions after refactoring") + + + +bool zen::fileExists(const Zstring& filePath) { + //symbolic links (broken or not) are also treated as existing files! #ifdef ZEN_WIN - WIN32_FIND_DATA linkInfo = {}; - const HANDLE searchHandle = ::FindFirstFile(applyLongPathPrefix(linkPath).c_str(), &linkInfo); - if (searchHandle != INVALID_HANDLE_VALUE) - { - ::FindClose(searchHandle); - return isSymlink(linkInfo); - } + const DWORD attr = ::GetFileAttributes(applyLongPathPrefix(filePath).c_str()); + if (attr != INVALID_FILE_ATTRIBUTES) + return (attr & FILE_ATTRIBUTE_DIRECTORY) == 0; //returns true for (file-)symlinks also #elif defined ZEN_LINUX || defined ZEN_MAC - struct ::stat linkInfo = {}; - if (::lstat(linkPath.c_str(), &linkInfo) == 0) - return S_ISLNK(linkInfo.st_mode); + struct ::stat fileInfo = {}; + if (::stat(filePath.c_str(), &fileInfo) == 0) //follow symlinks! + return S_ISREG(fileInfo.st_mode); #endif return false; } -bool zen::somethingExists(const Zstring& itemPath) +bool zen::dirExists(const Zstring& dirPath) { + //symbolic links (broken or not) are also treated as existing directories! #ifdef ZEN_WIN - const DWORD attr = ::GetFileAttributes(applyLongPathPrefix(itemPath).c_str()); + const DWORD attr = ::GetFileAttributes(applyLongPathPrefix(dirPath).c_str()); if (attr != INVALID_FILE_ATTRIBUTES) - return true; - const DWORD ec = ::GetLastError(); //copy before directly/indirectly making other system calls! - - //handle obscure file permission problem where ::GetFileAttributes() fails with ERROR_ACCESS_DENIED or ERROR_SHARING_VIOLATION - //while parent directory traversal is successful: e.g. "C:\pagefile.sys" - if (ec != ERROR_PATH_NOT_FOUND && //perf: short circuit for common "not existing" error codes - ec != ERROR_FILE_NOT_FOUND && // - ec != ERROR_BAD_NETPATH && // - ec != ERROR_BAD_NET_NAME) // - { - WIN32_FIND_DATA fileInfo = {}; - const HANDLE searchHandle = ::FindFirstFile(applyLongPathPrefix(itemPath).c_str(), &fileInfo); - if (searchHandle != INVALID_HANDLE_VALUE) - { - ::FindClose(searchHandle); - return true; - } - } + return (attr & FILE_ATTRIBUTE_DIRECTORY) != 0; //returns true for ((broken) dir-)symlinks also #elif defined ZEN_LINUX || defined ZEN_MAC - struct ::stat fileInfo = {}; - if (::lstat(itemPath.c_str(), &fileInfo) == 0) - return true; + struct ::stat dirInfo = {}; + if (::stat(dirPath.c_str(), &dirInfo) == 0) //follow symlinks! + return S_ISDIR(dirInfo.st_mode); #endif return false; } @@ -134,7 +252,15 @@ bool zen::somethingExists(const Zstring& itemPath) namespace { #ifdef ZEN_WIN -bool isFatDrive(const Zstring& filePath) //noexcept +enum class FatType +{ + NONE, + FAT, + FAT32, + EXFAT, +}; + +FatType getFatType(const Zstring& filePath) //noexcept { const DWORD bufferSize = MAX_PATH + 1; std::vector<wchar_t> buffer(bufferSize); @@ -145,7 +271,7 @@ bool isFatDrive(const Zstring& filePath) //noexcept bufferSize)) //__in DWORD cchBufferLength { assert(false); - return false; + return FatType::NONE; } const Zstring volumePath = appendSeparator(&buffer[0]); @@ -161,16 +287,21 @@ bool isFatDrive(const Zstring& filePath) //noexcept bufferSize)) //__in DWORD nFileSystemNameSize { assert(false); - return false; + return FatType::NONE; } - return &buffer[0] == Zstring(L"FAT") || - &buffer[0] == Zstring(L"FAT32"); + if (&buffer[0] == Zstring(L"FAT")) + return FatType::FAT; + if (&buffer[0] == Zstring(L"FAT32")) + return FatType::FAT32; + if (&buffer[0] == Zstring(L"exFAT")) + return FatType::EXFAT; + return FatType::NONE; } #ifdef ZEN_WIN_VISTA_AND_LATER -bool isFatDrive(HANDLE hFile) //noexcept +FatType getFatType(HANDLE hFile) //noexcept { const DWORD bufferSize = MAX_PATH + 1; //"The length of the file system name buffer, in TCHARs. The maximum buffer size is MAX_PATH + 1" std::vector<wchar_t> buffer(bufferSize); @@ -185,11 +316,16 @@ bool isFatDrive(HANDLE hFile) //noexcept bufferSize)) //_In_ DWORD nFileSystemNameSize { assert(false); - return false; + return FatType::NONE; } - return &buffer[0] == Zstring(L"FAT") || - &buffer[0] == Zstring(L"FAT32"); + if (&buffer[0] == Zstring(L"FAT")) + return FatType::FAT; + if (&buffer[0] == Zstring(L"FAT32")) + return FatType::FAT32; + if (&buffer[0] == Zstring(L"exFAT")) + return FatType::EXFAT; + return FatType::NONE; } #endif #endif @@ -331,7 +467,7 @@ Zstring zen::getTempFolderPath() //throw FileError } -bool zen::removeFile(const Zstring& filePath) //throw FileError +void zen::removeFilePlain(const Zstring& filePath) //throw FileError { #ifdef ZEN_WIN const wchar_t functionName[] = L"DeleteFile"; @@ -345,19 +481,14 @@ bool zen::removeFile(const Zstring& filePath) //throw FileError ErrorCode ec = getLastError(); //copy before directly/indirectly making other system calls! #ifdef ZEN_WIN if (ec == ERROR_ACCESS_DENIED) //function fails if file is read-only - { - ::SetFileAttributes(applyLongPathPrefix(filePath).c_str(), FILE_ATTRIBUTE_NORMAL); //(try to) normalize file attributes - - if (::DeleteFile(applyLongPathPrefix(filePath).c_str())) //now try again... - return true; - ec = ::GetLastError(); - } + if (::SetFileAttributes(applyLongPathPrefix(filePath).c_str(), FILE_ATTRIBUTE_NORMAL)) //(try to) normalize file attributes + { + if (::DeleteFile(applyLongPathPrefix(filePath).c_str())) //now try again... + return; + ec = ::GetLastError(); + } #endif - if (!somethingExists(filePath)) //warning: changes global error code!! - return false; //neither file nor any other object (e.g. broken symlink) with that name existing - caveat: what if "access is denied"!?!??!?!? - //begin of "regular" error reporting - const std::wstring errorMsg = replaceCpy(_("Cannot delete file %x."), L"%x", fmtPath(filePath)); std::wstring errorDescr = formatSystemError(functionName, ec); #ifdef ZEN_WIN_VISTA_AND_LATER @@ -369,18 +500,29 @@ bool zen::removeFile(const Zstring& filePath) //throw FileError errorDescr = _("The file is locked by another process:") + L"\n" + procList; } #endif - throw FileError(errorMsg, errorDescr); + throw FileError(replaceCpy(_("Cannot delete file %x."), L"%x", fmtPath(filePath)), errorDescr); } - return true; } -void zen::removeDirectorySimple(const Zstring& dirPath) //throw FileError +void zen::removeSymlinkPlain(const Zstring& linkPath) //throw FileError { #ifdef ZEN_WIN - //(try to) normalize file attributes: actually NEEDED for symbolic links also! - ::SetFileAttributes(applyLongPathPrefix(dirPath).c_str(), FILE_ATTRIBUTE_NORMAL); + const DWORD attr = ::GetFileAttributes(applyLongPathPrefix(linkPath).c_str()); + if (attr != INVALID_FILE_ATTRIBUTES && (attr & FILE_ATTRIBUTE_DIRECTORY) != 0) + removeDirectoryPlain(linkPath); //throw FileError + else + removeFilePlain(linkPath); //throw FileError +#elif defined ZEN_LINUX || defined ZEN_MAC + removeFilePlain(linkPath); //throw FileError +#endif +} + + +void zen::removeDirectoryPlain(const Zstring& dirPath) //throw FileError +{ +#ifdef ZEN_WIN const wchar_t functionName[] = L"RemoveDirectory"; if (!::RemoveDirectory(applyLongPathPrefix(dirPath).c_str())) #elif defined ZEN_LINUX || defined ZEN_MAC @@ -388,13 +530,20 @@ void zen::removeDirectorySimple(const Zstring& dirPath) //throw FileError if (::rmdir(dirPath.c_str()) != 0) #endif { - const ErrorCode ec = getLastError(); //copy before making other system calls! - - if (!somethingExists(dirPath)) //warning: changes global error code!! - return; + ErrorCode ec = getLastError(); //copy before making other system calls! +#ifdef ZEN_WIN + if (ec == ERROR_ACCESS_DENIED) //(try to) normalize file attributes: NEEDED! even folders and symlinks can have FILE_ATTRIBUTE_READONLY set! + if (::SetFileAttributes(applyLongPathPrefix(dirPath).c_str(), FILE_ATTRIBUTE_NORMAL)) + { + if (::RemoveDirectory(applyLongPathPrefix(dirPath).c_str())) //now try again... + return; + ec = ::GetLastError(); + } +#elif defined ZEN_LINUX || defined ZEN_MAC + bool symlinkExists = false; + try { symlinkExists = getItemType(dirPath) == ItemType::SYMLINK; } /*throw FileError*/ catch (FileError&) {} //previous exception is more relevant -#if defined ZEN_LINUX || defined ZEN_MAC - if (symlinkExists(dirPath)) + if (symlinkExists) { if (::unlink(dirPath.c_str()) != 0) THROW_LAST_FILE_ERROR(replaceCpy(_("Cannot delete directory %x."), L"%x", fmtPath(dirPath)), L"unlink"); @@ -418,48 +567,41 @@ namespace { void removeDirectoryImpl(const Zstring& folderPath) //throw FileError { - assert(dirExists(folderPath)); //[!] no symlinks in this context!!! - //attention: check if folderPath is a symlink! Do NOT traverse into it deleting contained files!!! - +#ifndef NDEBUG //[!] no symlinks in this context!!! Do NOT traverse into it deleting contained files!!! + try { assert(getItemType(folderPath) != ItemType::SYMLINK); /*throw FileError*/ } + catch (FileError&) {} +#endif std::vector<Zstring> filePaths; - std::vector<Zstring> folderSymlinkPaths; + std::vector<Zstring> symlinkPaths; std::vector<Zstring> folderPaths; //get all files and directories from current directory (WITHOUT subdirectories!) 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 - folderSymlinkPaths.push_back(si.fullPath); - else //file symlink, broken symlink -#endif - filePaths.push_back(si.fullPath); - }, + [&](const FileInfo& fi) { filePaths .push_back(fi.fullPath); }, + [&](const FolderInfo& fi) { folderPaths .push_back(fi.fullPath); }, //defer recursion => save stack space and allow deletion of extremely deep hierarchies! + [&](const SymlinkInfo& si) { symlinkPaths.push_back(si.fullPath); }, [](const std::wstring& errorMsg) { throw FileError(errorMsg); }); for (const Zstring& filePath : filePaths) - removeFile(filePath); //throw FileError + removeFilePlain(filePath); //throw FileError - for (const Zstring& symlinkPath : folderSymlinkPaths) - removeDirectorySimple(symlinkPath); //throw FileError + for (const Zstring& symlinkPath : symlinkPaths) + removeSymlinkPlain(symlinkPath); //throw FileError //delete directories recursively for (const Zstring& subFolderPath : folderPaths) removeDirectoryImpl(subFolderPath); //throw FileError; call recursively to correctly handle symbolic links - removeDirectorySimple(folderPath); //throw FileError + removeDirectoryPlain(folderPath); //throw FileError } } -void zen::removeDirectoryRecursively(const Zstring& dirPath) //throw FileError +void zen::removeDirectoryPlainRecursion(const Zstring& dirPath) //throw FileError { - if (symlinkExists(dirPath)) - removeDirectorySimple(dirPath); //throw FileError - else if (somethingExists(dirPath)) + if (getItemType(dirPath) == ItemType::SYMLINK) //throw FileError + removeSymlinkPlain(dirPath); //throw FileError + else removeDirectoryImpl(dirPath); //throw FileError } @@ -524,7 +666,6 @@ void renameFile_sub(const Zstring& pathSource, const Zstring& pathTarget) //thro errorDescr = _("The file is locked by another process:") + L"\n" + procList; } #endif - if (ec == ERROR_NOT_SAME_DEVICE) throw ErrorDifferentVolume(errorMsg, errorDescr); if (ec == ERROR_ALREADY_EXISTS || //-> used on Win7 x64 @@ -534,7 +675,8 @@ 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 (atomically) overwrite! => equivalent to SetFileInformationByHandle() + FILE_RENAME_INFO::ReplaceIfExists + //rename() will never fail with EEXIST, but always (atomically) overwrite! + //=> equivalent to SetFileInformationByHandle() + FILE_RENAME_INFO::ReplaceIfExists or ::MoveFileEx + MOVEFILE_REPLACE_EXISTING //=> Linux: renameat2() with RENAME_NOREPLACE -> still new, probably buggy //=> OS X: no solution @@ -550,9 +692,15 @@ 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 (somethingExists(pathTarget)) + if (!equalFilePath(pathSource, pathTarget)) //exception for OS X: changing file name case is not an "already exists" situation! + { + bool alreadyExists = true; + try { /*ItemType type = */getItemType(pathTarget); } /*throw FileError*/ catch (FileError&) { alreadyExists = false; } + + if (alreadyExists) throwException(EEXIST); + //else: nothing exists or other error (hopefully ::rename will also fail!) + } if (::rename(pathSource.c_str(), pathTarget.c_str()) != 0) throwException(errno); @@ -585,10 +733,11 @@ Zstring getFilenameFmt(const Zstring& filePath, Function fun) //throw(); returns } -Zstring findUnused8Dot3Name(const Zstring& filePath) //find a unique 8.3 short name +Zstring findUnused8Dot3Name(const Zstring& filePath) //throw FileError { - const Zstring pathPrefix = contains(filePath, FILE_NAME_SEPARATOR) ? - (beforeLast(filePath, FILE_NAME_SEPARATOR, IF_MISSING_RETURN_NONE) + FILE_NAME_SEPARATOR) : Zstring(); + Opt<Zstring> parentPath = getParentFolderPath(filePath); + assert(parentPath); + const Zstring pathPrefix = parentPath ? appendSeparator(*parentPath) : Zstring(); //extension needn't contain reasonable data Zstring extension = getFileExtension(filePath); @@ -599,35 +748,35 @@ Zstring findUnused8Dot3Name(const Zstring& filePath) //find a unique 8.3 short n for (int index = 0; index < 100000000; ++index) //filePath must be representable by <= 8 characters { - const Zstring output = pathPrefix + numberTo<Zstring>(index) + Zchar('.') + extension; - if (!somethingExists(output)) //ensure uniqueness - return output; + const Zstring testPath = pathPrefix + numberTo<Zstring>(index) + Zchar('.') + extension; + if (!getItemTypeIfExists(testPath)) //throw FileError + return testPath; } 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__)); } -bool have8dot3NameClash(const Zstring& filePath) +bool have8dot3NameClash(const Zstring& itemPath) { - if (!contains(filePath, FILE_NAME_SEPARATOR)) - return false; + try + { + /*ItemType type =*/ getItemType(itemPath); //throw FileError + } + catch (FileError&) { return false; } - if (somethingExists(filePath)) //name OR directory! + const Zstring nameOrig = afterLast(itemPath, FILE_NAME_SEPARATOR, IF_MISSING_RETURN_ALL); + const Zstring nameShort = afterLast(getFilenameFmt(itemPath, ::GetShortPathName), FILE_NAME_SEPARATOR, IF_MISSING_RETURN_ALL); //throw() returns empty string on error + const Zstring nameLong = afterLast(getFilenameFmt(itemPath, ::GetLongPathName ), FILE_NAME_SEPARATOR, IF_MISSING_RETURN_ALL); // + + if (!nameShort.empty() && + !nameLong .empty() && + equalFilePath(nameOrig, nameShort) && + !equalFilePath(nameShort, nameLong)) { - const Zstring origName = afterLast(filePath, FILE_NAME_SEPARATOR, IF_MISSING_RETURN_ALL); - const Zstring shortName = afterLast(getFilenameFmt(filePath, ::GetShortPathName), FILE_NAME_SEPARATOR, IF_MISSING_RETURN_ALL); //throw() returns empty string on error - const Zstring longName = afterLast(getFilenameFmt(filePath, ::GetLongPathName ), FILE_NAME_SEPARATOR, IF_MISSING_RETURN_ALL); // - - if (!shortName.empty() && - !longName .empty() && - 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" - return true; - } + //for itemPath short and long file name are equal and another unrelated file happens to have the same short name + //e.g. itemPath == "TESTWE~1", but another file is existing named "TestWeb" with short name ""TESTWE~1" + return true; } return false; } @@ -639,16 +788,16 @@ public: { const Zstring longName = afterLast(getFilenameFmt(filePath, ::GetLongPathName), FILE_NAME_SEPARATOR, IF_MISSING_RETURN_ALL); //throw() returns empty string on error - unrelatedFile = beforeLast(filePath, FILE_NAME_SEPARATOR, IF_MISSING_RETURN_NONE); - if (!unrelatedFile.empty()) - unrelatedFile += FILE_NAME_SEPARATOR; - unrelatedFile += longName; + Opt<Zstring> parentPath = getParentFolderPath(filePath); + assert(parentPath); + unrelatedFilePath_ = parentPath ? appendSeparator(*parentPath) : Zstring(); + unrelatedFilePath_ += longName; //find another name in short format: this ensures the actual short name WILL be renamed as well! - unrelatedFileParked = findUnused8Dot3Name(filePath); + parkedFilePath_ = findUnused8Dot3Name(filePath); //throw FileError //move already existing short name out of the way for now - renameFile_sub(unrelatedFile, unrelatedFileParked); //throw FileError, ErrorDifferentVolume + renameFile_sub(unrelatedFilePath_, parkedFilePath_); //throw FileError, ErrorDifferentVolume //DON'T call renameFile() to avoid reentrance! } @@ -657,13 +806,13 @@ public: //the file system should assign this unrelated file a new (unique) short name try { - renameFile_sub(unrelatedFileParked, unrelatedFile); //throw FileError, ErrorDifferentVolume + renameFile_sub(parkedFilePath_, unrelatedFilePath_); //throw FileError, ErrorDifferentVolume } catch (FileError&) {} } private: - Zstring unrelatedFile; - Zstring unrelatedFileParked; + Zstring unrelatedFilePath_; + Zstring parkedFilePath_; }; #endif } @@ -752,51 +901,52 @@ void setFileTimeByHandle(HANDLE hFile, //throw FileError std::wstring errorMsg = replaceCpy(_("Cannot write modification time of %x."), L"%x", fmtPath(filePath)); //add more meaningful message: FAT accepts only a subset of the NTFS date range + if (ec == ERROR_INVALID_PARAMETER) #ifdef ZEN_WIN_VISTA_AND_LATER - if (ec == ERROR_INVALID_PARAMETER && isFatDrive(hFile)) + if (getFatType(hFile) != FatType::NONE) //exFAT has the same date range like FAT/FAT32: http://www.freefilesync.org/forum/viewtopic.php?t=4051 #else - if (ec == ERROR_INVALID_PARAMETER && isFatDrive(filePath)) + if (getFatType(filePath) != FatType::NONE) #endif - { - //let's not fail due to an invalid creation time on FAT: http://www.freefilesync.org/forum/viewtopic.php?t=2278 - if (creationTime) //retry (single-level recursion at most!) - return setFileTimeByHandle(hFile, nullptr, lastWriteTime, filePath); //throw FileError - - //if the ERROR_INVALID_PARAMETER is due to an invalid date, enhance message: - const LARGE_INTEGER writeTimeInt = toLargeInteger(lastWriteTime); - if (writeTimeInt.QuadPart < 0x01a8e79fe1d58000 || //1980-01-01 https://en.wikipedia.org/wiki/Time_formatting_and_storage_bugs#Year_2100 - writeTimeInt.QuadPart >= 0x022f716377640000) //2100-01-01 { - errorMsg += L"\nA FAT volume can only store dates from 1980 to 2099:\n" "\twrite time (UTC):"; - SYSTEMTIME st = {}; - if (::FileTimeToSystemTime(&lastWriteTime, //__in const FILETIME *lpFileTime, - &st)) //__out LPSYSTEMTIME lpSystemTime + //let's not fail due to an invalid creation time on FAT: http://www.freefilesync.org/forum/viewtopic.php?t=2278 + if (creationTime) //retry (single-level recursion at most!) + return setFileTimeByHandle(hFile, nullptr, lastWriteTime, filePath); //throw FileError + + //if the ERROR_INVALID_PARAMETER is due to an invalid date, enhance message: + const LARGE_INTEGER writeTimeInt = toLargeInteger(lastWriteTime); + if (writeTimeInt.QuadPart < 0x01a8e79fe1d58000 || //1980-01-01 https://en.wikipedia.org/wiki/Time_formatting_and_storage_bugs#Year_2100 + writeTimeInt.QuadPart >= 0x022f716377640000) //2100-01-01 { - //we need a low-level reliable routine to format a potentially invalid date => don't use strftime!!! - int bufferSize = ::GetDateFormat(LOCALE_USER_DEFAULT, 0, &st, nullptr, nullptr, 0); - if (bufferSize > 0) - { - std::vector<wchar_t> buffer(bufferSize); - if (::GetDateFormat(LOCALE_USER_DEFAULT, //_In_ LCID Locale, - 0, //_In_ DWORD dwFlags, - &st, //_In_opt_ const SYSTEMTIME *lpDate, - nullptr, //_In_opt_ LPCTSTR lpFormat, - &buffer[0], //_Out_opt_ LPTSTR lpDateStr, - bufferSize) > 0) //_In_ int cchDate - errorMsg += std::wstring(L" ") + &buffer[0]; //GetDateFormat() returns char count *including* 0-termination! - } - - bufferSize = ::GetTimeFormat(LOCALE_USER_DEFAULT, 0, &st, nullptr, nullptr, 0); - if (bufferSize > 0) + errorMsg += L"\nA FAT volume can only store dates from 1980 to 2099:\n" "\twrite time (UTC):"; + SYSTEMTIME st = {}; + if (::FileTimeToSystemTime(&lastWriteTime, //__in const FILETIME *lpFileTime, + &st)) //__out LPSYSTEMTIME lpSystemTime { - std::vector<wchar_t> buffer(bufferSize); - if (::GetTimeFormat(LOCALE_USER_DEFAULT, 0, &st, nullptr, &buffer[0], bufferSize) > 0) - errorMsg += std::wstring(L" ") + &buffer[0]; //GetDateFormat() returns char count *including* 0-termination! + //we need a low-level reliable routine to format a potentially invalid date => don't use strftime!!! + int bufferSize = ::GetDateFormat(LOCALE_USER_DEFAULT, 0, &st, nullptr, nullptr, 0); + if (bufferSize > 0) + { + std::vector<wchar_t> buffer(bufferSize); + if (::GetDateFormat(LOCALE_USER_DEFAULT, //_In_ LCID Locale, + 0, //_In_ DWORD dwFlags, + &st, //_In_opt_ const SYSTEMTIME *lpDate, + nullptr, //_In_opt_ LPCTSTR lpFormat, + &buffer[0], //_Out_opt_ LPTSTR lpDateStr, + bufferSize) > 0) //_In_ int cchDate + errorMsg += std::wstring(L" ") + &buffer[0]; //GetDateFormat() returns char count *including* 0-termination! + } + + bufferSize = ::GetTimeFormat(LOCALE_USER_DEFAULT, 0, &st, nullptr, nullptr, 0); + if (bufferSize > 0) + { + std::vector<wchar_t> buffer(bufferSize); + if (::GetTimeFormat(LOCALE_USER_DEFAULT, 0, &st, nullptr, &buffer[0], bufferSize) > 0) + errorMsg += std::wstring(L" ") + &buffer[0]; //GetDateFormat() returns char count *including* 0-termination! + } } + errorMsg += L" (" + numberTo<std::wstring>(writeTimeInt.QuadPart) + L")"; //just in case the above date formatting fails } - errorMsg += L" (" + numberTo<std::wstring>(writeTimeInt.QuadPart) + L")"; //just in case the above date formatting fails } - } throw FileError(errorMsg, formatSystemError(L"SetFileTime", ec)); } @@ -1200,9 +1350,12 @@ void copyItemPermissions(const Zstring& sourcePath, const Zstring& targetPath, P //in contrast to ::SetSecurityInfo(), ::SetFileSecurity() seems to honor the "inherit DACL/SACL" flags //CAVEAT: if a file system does not support ACLs, GetFileSecurity() will return successfully with a *valid* security descriptor containing *no* ACL entries! + const bool isSymlinkSource = getItemType(sourcePath) == ItemType::SYMLINK; //throw FileError + const bool isSymlinkTarget = getItemType(targetPath) == ItemType::SYMLINK; //throw FileError + //NOTE: ::GetFileSecurity()/::SetFileSecurity() do NOT follow Symlinks! getResolvedSymlinkPath() requires Vista or later! - const Zstring sourceResolved = procSl == ProcSymlink::FOLLOW && symlinkExists(sourcePath) ? getResolvedSymlinkPath(sourcePath) : sourcePath; //throw FileError - const Zstring targetResolved = procSl == ProcSymlink::FOLLOW && symlinkExists(targetPath) ? getResolvedSymlinkPath(targetPath) : targetPath; // + const Zstring sourceResolved = procSl == ProcSymlink::FOLLOW && isSymlinkSource ? getResolvedSymlinkPath(sourcePath) : sourcePath; //throw FileError + const Zstring targetResolved = procSl == ProcSymlink::FOLLOW && isSymlinkTarget ? getResolvedSymlinkPath(targetPath) : targetPath; // //setting privileges requires admin rights! try @@ -1371,7 +1524,8 @@ void copyItemPermissions(const Zstring& sourcePath, const Zstring& targetPath, P if (::lchown(targetPath.c_str(), fileInfo.st_uid, fileInfo.st_gid) != 0) // may require admin rights! THROW_LAST_FILE_ERROR(replaceCpy(_("Cannot write permissions of %x."), L"%x", fmtPath(targetPath)), L"lchown"); - if (!symlinkExists(targetPath) && //setting access permissions doesn't make sense for symlinks on Linux: there is no lchmod() + const bool isSymlinkTarget = getItemType(targetPath) == ItemType::SYMLINK; //throw FileError + if (!isSymlinkTarget && //setting access permissions doesn't make sense for symlinks on Linux: there is no lchmod() ::chmod(targetPath.c_str(), fileInfo.st_mode) != 0) THROW_LAST_FILE_ERROR(replaceCpy(_("Cannot write permissions of %x."), L"%x", fmtPath(targetPath)), L"chmod"); } @@ -1405,66 +1559,46 @@ void copyItemPermissions(const Zstring& sourcePath, const Zstring& targetPath, P } #endif } +} -void makeDirectoryRecursivelyImpl(const Zstring& dirPath) //FileError +void zen::createDirectoryIfMissingRecursion(const Zstring& dirPath) //throw FileError { - assert(!endsWith(dirPath, FILE_NAME_SEPARATOR)); //even "C:\" should be "C:" as input! + if (!getParentFolderPath(dirPath)) //device root + return static_cast<void>(/*ItemType =*/ getItemType(dirPath)); //throw FileError try { - copyNewDirectory(Zstring(), dirPath, false /*copyFilePermissions*/); //throw FileError, ErrorTargetExisting, ErrorTargetPathMissing + copyNewDirectory(Zstring(), dirPath, false /*copyFilePermissions*/); //throw FileError, ErrorTargetExisting } - catch (const ErrorTargetExisting&) {} //*something* existing: folder or FILE! - catch (const ErrorTargetPathMissing&) + catch (FileError&) { - //we need to create parent directories first - const Zstring parentPath = beforeLast(dirPath, FILE_NAME_SEPARATOR, IF_MISSING_RETURN_NONE); - if (!parentPath.empty()) - { - //recurse... - makeDirectoryRecursivelyImpl(parentPath); //throw FileError + Opt<PathDetails> pd; + try { pd = getPathDetails(dirPath); /*throw FileError*/ } + catch (FileError&) {} //previous exception is more relevant - //now try again... - copyNewDirectory(Zstring(), dirPath, false /*copyFilePermissions*/); //throw FileError, (ErrorTargetExisting), (ErrorTargetPathMissing) + if (pd && pd->existingType != ItemType::FILE) + { + Zstring intermediatePath = pd->existingPath; + for (const Zstring& itemName : pd->relPath) + { + intermediatePath = appendSeparator(intermediatePath) + itemName; + copyNewDirectory(Zstring(), intermediatePath, false /*copyFilePermissions*/); //throw FileError, (ErrorTargetExisting) + } return; } throw; } } -} - - -void zen::makeDirectoryRecursively(const Zstring& dirPath) //throw FileError -{ - //remove trailing separator (even for C:\ root directories) - const Zstring dirFormatted = endsWith(dirPath, FILE_NAME_SEPARATOR) ? - beforeLast(dirPath, FILE_NAME_SEPARATOR, IF_MISSING_RETURN_NONE) : - dirPath; - makeDirectoryRecursivelyImpl(dirFormatted); //FileError -} //source path is optional (may be empty) -void zen::copyNewDirectory(const Zstring& sourcePath, const Zstring& targetPath, //throw FileError, ErrorTargetExisting, ErrorTargetPathMissing +void zen::copyNewDirectory(const Zstring& sourcePath, const Zstring& targetPath, //throw FileError, ErrorTargetExisting bool copyFilePermissions) { #ifdef ZEN_WIN 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! - const Zstring pathPf = appendSeparator(targetPath); //we do not support "C:" to represent a relative path! - if (pathPf.size() == 3 && - isAlpha(pathPf[0]) && pathPf[1] == L':') - { - const DWORD ec = somethingExists(pathPf) ? ERROR_ALREADY_EXISTS : ERROR_PATH_NOT_FOUND; //don't use dirExists() => harmonize with ErrorTargetExisting! - const std::wstring errorDescr = formatSystemError(L"CreateDirectory", ec); - - if (ec == ERROR_ALREADY_EXISTS) - throw ErrorTargetExisting(getErrorMsg(pathPf), errorDescr); - throw FileError(getErrorMsg(pathPf), 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'.')) @@ -1498,8 +1632,9 @@ void zen::copyNewDirectory(const Zstring& sourcePath, const Zstring& targetPath, if (ec == ERROR_ALREADY_EXISTS) throw ErrorTargetExisting(getErrorMsg(targetPath), errorDescr); - else if (ec == ERROR_PATH_NOT_FOUND) - throw ErrorTargetPathMissing(getErrorMsg(targetPath), errorDescr); + //else if (ec == ERROR_PATH_NOT_FOUND || //the ususal suspect + // ec == ERROR_FILE_NOT_FOUND) //Webdav incorrectly returns this one: http://www.freefilesync.org/forum/viewtopic.php?t=4053 + // throw ErrorTargetPathMissing(getErrorMsg(targetPath), errorDescr); throw FileError(getErrorMsg(targetPath), errorDescr); } } @@ -1524,8 +1659,8 @@ void zen::copyNewDirectory(const Zstring& sourcePath, const Zstring& targetPath, if (lastError == EEXIST) throw ErrorTargetExisting(errorMsg, errorDescr); - else if (lastError == ENOENT) - throw ErrorTargetPathMissing(errorMsg, errorDescr); + //else if (lastError == ENOENT) + // throw ErrorTargetPathMissing(errorMsg, errorDescr); throw FileError(errorMsg, errorDescr); } #endif @@ -1610,7 +1745,7 @@ void zen::copyNewDirectory(const Zstring& sourcePath, const Zstring& targetPath, catch (FileError&) {} #endif - ZEN_ON_SCOPE_FAIL(try { removeDirectorySimple(targetPath); } + ZEN_ON_SCOPE_FAIL(try { removeDirectoryPlain(targetPath); } catch (FileError&) {}); //ensure cleanup: //enforce copying file permissions: it's advertized on GUI... @@ -1625,11 +1760,13 @@ void zen::copySymlink(const Zstring& sourceLink, const Zstring& targetLink, bool const Zstring linkPath = getSymlinkTargetRaw(sourceLink); //throw FileError; accept broken symlinks #ifdef ZEN_WIN - const bool isDirLink = [&]() -> bool - { - const DWORD ret = ::GetFileAttributes(applyLongPathPrefix(sourceLink).c_str()); - return ret != INVALID_FILE_ATTRIBUTES && (ret & FILE_ATTRIBUTE_DIRECTORY); - }(); + WIN32_FILE_ATTRIBUTE_DATA sourceAttr = {}; + if (!::GetFileAttributesEx(applyLongPathPrefix(sourceLink).c_str(), //__in LPCTSTR lpFileName, + GetFileExInfoStandard, //__in GET_FILEEX_INFO_LEVELS fInfoLevelId, + &sourceAttr)) //__out LPVOID lpFileInformation + THROW_LAST_FILE_ERROR(replaceCpy(_("Cannot read file attributes of %x."), L"%x", fmtPath(sourceLink)), L"GetFileAttributesEx"); + + const bool isDirLink = (sourceAttr.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0; using CreateSymbolicLinkFunc = BOOLEAN (WINAPI*)(LPCTSTR lpSymlinkFileName, LPCTSTR lpTargetFileName, DWORD dwFlags); const SysDllFun<CreateSymbolicLinkFunc> createSymbolicLink(L"kernel32.dll", "CreateSymbolicLinkW"); @@ -1649,30 +1786,11 @@ 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! - - auto cleanUp = [&] - { - try - { -#ifdef ZEN_WIN - if (isDirLink) - removeDirectorySimple(targetLink); //throw FileError - else -#endif - removeFile(targetLink); //throw FileError - } - catch (FileError&) {} - }; - ZEN_ON_SCOPE_FAIL(cleanUp()); + ZEN_ON_SCOPE_FAIL(try { removeSymlinkPlain(targetLink); /*throw FileError*/ } + catch (FileError&) {}); //file times: essential for sync'ing a symlink: enforce this! (don't just try!) #ifdef ZEN_WIN - WIN32_FILE_ATTRIBUTE_DATA sourceAttr = {}; - if (!::GetFileAttributesEx(applyLongPathPrefix(sourceLink).c_str(), //__in LPCTSTR lpFileName, - GetFileExInfoStandard, //__in GET_FILEEX_INFO_LEVELS fInfoLevelId, - &sourceAttr)) //__out LPVOID lpFileInformation - THROW_LAST_FILE_ERROR(replaceCpy(_("Cannot read file attributes of %x."), L"%x", fmtPath(sourceLink)), L"GetFileAttributesEx"); - setWriteTimeNative(targetLink, sourceAttr.ftLastWriteTime, &sourceAttr.ftCreationTime, ProcSymlink::DIRECT); //throw FileError #else @@ -1924,8 +2042,6 @@ InSyncAttributes copyFileWindowsStream(const Zstring& sourceFile, //throw FileEr ec == ERROR_ALREADY_EXISTS) //comment on msdn claims, this one is used on Windows Mobile 6 throw ErrorTargetExisting(errorMsg, errorDescr); - //if (ec == ERROR_PATH_NOT_FOUND) throw ErrorTargetPathMissing(errorMsg, errorDescr); - throw FileError(errorMsg, errorDescr); } #ifdef ZEN_WIN_VISTA_AND_LATER @@ -1943,8 +2059,8 @@ InSyncAttributes copyFileWindowsStream(const Zstring& sourceFile, //throw FileEr assert(false); ); #else - ZEN_ON_SCOPE_FAIL(try { removeFile(targetFile); } - catch (FileError&) {} ); //transactional behavior: guard just after opening target and before managing hFileTarget + ZEN_ON_SCOPE_FAIL(try { removeFilePlain(targetFile); } + catch (FileError&) {} ); //transactional behavior: guard just after opening target and before managing hFileTarget FileOutput fileOut(hFileTarget, targetFile); //pass ownership #endif @@ -2071,7 +2187,7 @@ InSyncAttributes copyFileWindowsStream(const Zstring& sourceFile, //throw FileEr } //time needs to be set at the end: WriteFile/BackupWrite() change modification time - setFileTimeByHandle(fileOut.getHandle(), &sourceInfo.ftCreationTime,sourceInfo.ftLastWriteTime, targetFile); //throw FileError + setFileTimeByHandle(fileOut.getHandle(), &sourceInfo.ftCreationTime, sourceInfo.ftLastWriteTime, targetFile); //throw FileError return newAttrib; } @@ -2206,7 +2322,7 @@ InSyncAttributes copyFileWindowsDefault(const Zstring& sourceFile, //throw FileE try { activatePrivilege(PrivilegeName::RESTORE); } catch (const FileError&) { backupPrivilegesActive = false; } - auto guardTarget = zen::makeGuard<ScopeGuardRunMode::ON_FAIL>([&] { try { removeFile(targetFile); } catch (FileError&) {} }); + auto guardTarget = zen::makeGuard<ScopeGuardRunMode::ON_FAIL>([&] { try { removeFilePlain(targetFile); } catch (FileError&) {} }); //transactional behavior: guard just before starting copy, we don't trust ::CopyFileEx(), do we? ;) DWORD copyFlags = COPY_FILE_FAIL_IF_EXISTS; @@ -2277,16 +2393,20 @@ InSyncAttributes copyFileWindowsDefault(const Zstring& sourceFile, //throw FileE throw ErrorTargetExisting(errorMsg, errorDescr); } - //if (lastError == ERROR_PATH_NOT_FOUND) throw ErrorTargetPathMissing(errorMsg, errorDescr); //could this also be source path missing!? + //lastError == ERROR_PATH_NOT_FOUND: could this also be source path missing!? try //add more meaningful message { //trying to copy > 4GB file to FAT/FAT32 volume gives obscure ERROR_INVALID_PARAMETER (FAT can indeed handle files up to 4 Gig, tested!) - if (ec == ERROR_INVALID_PARAMETER && - isFatDrive(targetFile) && - getFilesize(sourceFile) >= 4U * std::uint64_t(1024U * 1024 * 1024)) //throw FileError - errorDescr += L"\nFAT volumes cannot store files larger than 4 gigabytes."; - //see "Limitations of the FAT32 File System": http://support.microsoft.com/kb/314463/en-us + if (ec == ERROR_INVALID_PARAMETER) + { + const FatType fatType = getFatType(targetFile); + if ((fatType == FatType::FAT || + fatType == FatType::FAT32) && //no problem for exFAT (limit ca. 128 PB) + getFilesize(sourceFile) >= 4U * std::uint64_t(1024U * 1024 * 1024)) //throw FileError + errorDescr += L"\nFAT volumes cannot store files larger than 4 gigabytes."; + //see "Limitations of the FAT32 File System": http://support.microsoft.com/kb/314463/en-us + } //note: ERROR_INVALID_PARAMETER can also occur when copying to a SharePoint server or MS SkyDrive and the target file path is of a restricted type. } @@ -2378,7 +2498,7 @@ InSyncAttributes copyFileOsSpecific(const Zstring& sourceFile, //throw FileError throw FileError(errorMsg, errorDescr); } - ZEN_ON_SCOPE_FAIL( try { removeFile(targetFile); } + ZEN_ON_SCOPE_FAIL( try { removeFilePlain(targetFile); } catch (FileError&) {} ); //transactional behavior: place guard after ::open() and before lifetime of FileOutput: //=> don't delete file that existed previously!!! @@ -2459,7 +2579,7 @@ InSyncAttributes zen::copyNewFile(const Zstring& sourceFile, const Zstring& targ const InSyncAttributes attr = copyFileOsSpecific(sourceFile, targetFile, notifyProgress); //throw FileError, ErrorTargetExisting, ErrorFileLocked //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); } + ZEN_ON_SCOPE_FAIL(try { removeFilePlain(targetFile); } catch (FileError&) {}); if (copyFilePermissions) diff --git a/zen/file_access.h b/zen/file_access.h index fdb787bd..0586ea8f 100644 --- a/zen/file_access.h +++ b/zen/file_access.h @@ -17,17 +17,40 @@ namespace zen { //note: certain functions require COM initialization! (vista_file_op.h) +Opt<Zstring> getParentFolderPath(const Zstring& itemPath); + +//POSITIVE existence checks; if negative: 1. item not existing 2. different type 3.access error or similar +bool fileAvailable(const Zstring& filePath); //noexcept +bool dirAvailable (const Zstring& dirPath ); // + + bool fileExists (const Zstring& filePath); //noexcept; check whether file or file-symlink exists bool dirExists (const Zstring& dirPath ); //noexcept; check whether directory or dir-symlink exists -bool symlinkExists (const Zstring& linkPath); //noexcept; check whether a symbolic link exists -bool somethingExists(const Zstring& itemPath); //noexcept; check whether any object with this name exists + +enum class ItemType +{ + FILE, + FOLDER, + SYMLINK, +}; +//(hopefully) fast: does not distinguish between error/not existing +ItemType getItemType (const Zstring& itemPath); //throw FileError +//execute potentially SLOW folder traversal but distinguish error/not existing +Opt<ItemType> getItemTypeIfExists(const Zstring& itemPath); //throw FileError + +struct PathDetails +{ + ItemType existingType; + Zstring existingPath; //itemPath =: existingPath + relPath + std::vector<Zstring> relPath; // +}; +PathDetails getPathDetails(const Zstring& itemPath); //throw FileError enum class ProcSymlink { DIRECT, FOLLOW }; - void setFileTime(const Zstring& filePath, std::int64_t modificationTime, ProcSymlink procSl); //throw FileError //symlink handling: always evaluate target @@ -37,11 +60,10 @@ VolumeId getVolumeId(const Zstring& itemPath); //throw FileError //get per-user directory designated for temporary files: Zstring getTempFolderPath(); //throw FileError -bool removeFile(const Zstring& filePath); //throw FileError; return "false" if file is not existing - -void removeDirectorySimple(const Zstring& dirPath); //throw FileError - -void removeDirectoryRecursively(const Zstring& dirPath); //throw FileError +void removeFilePlain (const Zstring& filePath); //throw FileError; ERROR if not existing +void removeSymlinkPlain (const Zstring& linkPath); //throw FileError; ERROR if not existing +void removeDirectoryPlain(const Zstring& dirPath ); //throw FileError; ERROR if not existing +void removeDirectoryPlainRecursion(const Zstring& dirPath); //throw FileError; ERROR if not existing //rename file or directory: no copying!!! void renameFile(const Zstring& itemPathOld, const Zstring& itemPathNew); //throw FileError, ErrorDifferentVolume, ErrorTargetExisting @@ -50,11 +72,11 @@ bool supportsPermissions(const Zstring& dirPath); //throw FileError, dereference //- no error if already existing //- create recursively if parent directory is not existing -void makeDirectoryRecursively(const Zstring& dirPath); //throw FileError +void createDirectoryIfMissingRecursion(const Zstring& dirPath); //throw FileError //fail if already existing or parent directory not existing: //source path is optional (may be empty) -void copyNewDirectory(const Zstring& sourcePath, const Zstring& targetPath, bool copyFilePermissions); //throw FileError, ErrorTargetExisting, ErrorTargetPathMissing +void copyNewDirectory(const Zstring& sourcePath, const Zstring& targetPath, bool copyFilePermissions); //throw FileError, ErrorTargetExisting void copySymlink(const Zstring& sourceLink, const Zstring& targetLink, bool copyFilePermissions); //throw FileError diff --git a/zen/file_error.h b/zen/file_error.h index 374d0b2c..aa41040d 100644 --- a/zen/file_error.h +++ b/zen/file_error.h @@ -31,7 +31,7 @@ private: #define DEFINE_NEW_FILE_ERROR(X) struct X : public FileError { X(const std::wstring& msg) : FileError(msg) {} X(const std::wstring& msg, const std::wstring& descr) : FileError(msg, descr) {} }; DEFINE_NEW_FILE_ERROR(ErrorTargetExisting); -DEFINE_NEW_FILE_ERROR(ErrorTargetPathMissing); +//DEFINE_NEW_FILE_ERROR(ErrorTargetPathMissing); DEFINE_NEW_FILE_ERROR(ErrorFileLocked); DEFINE_NEW_FILE_ERROR(ErrorDifferentVolume); diff --git a/zen/file_traverser.cpp b/zen/file_traverser.cpp index 3eb284e1..ef6d255c 100644 --- a/zen/file_traverser.cpp +++ b/zen/file_traverser.cpp @@ -28,8 +28,8 @@ using namespace zen; void zen::traverseFolder(const Zstring& dirPath, const std::function<void (const FileInfo& fi)>& onFile, - const std::function<void (const DirInfo& di)>& onDir, - const std::function<void (const SymlinkInfo& si)>& onLink, + const std::function<void (const FolderInfo& fi)>& onFolder, + const std::function<void (const SymlinkInfo& si)>& onSymlink, const std::function<void (const std::wstring& errorMsg)>& onError) { try @@ -41,12 +41,15 @@ void zen::traverseFolder(const Zstring& dirPath, { const DWORD ec = ::GetLastError(); //copy before directly/indirectly making other system calls! if (ec == ERROR_FILE_NOT_FOUND) - { - //1. directory may not exist *or* 2. it is completely empty: not all directories contain "., .." entries, e.g. a drive's root directory; NetDrive - // -> FindFirstFile() is a nice example of violation of API design principle of single responsibility - if (dirExists(dirPath)) //yes, a race-condition, still the best we can do - return; - } + try + { + //1. directory may not exist *or* 2. it is completely empty: not all directories contain "., .." entries, e.g. a drive's root directory; NetDrive + // -> FindFirstFile() is a nice example of violating the API design principle of single responsibility + if (getItemType(dirPath) == ItemType::FOLDER) //throw FileError + return; + } + catch (FileError&) {} //previous exception is more relevant + throw FileError(replaceCpy(_("Cannot open directory %x."), L"%x", fmtPath(dirPath)), formatSystemError(L"FindFirstFile", ec)); } ZEN_ON_SCOPE_EXIT(::FindClose(hDir)); @@ -75,22 +78,23 @@ void zen::traverseFolder(const Zstring& dirPath, if (itemNameRaw[0] == 0) throw FileError(replaceCpy(_("Cannot read directory %x."), L"%x", fmtPath(dirPath)), L"FindNextFile: Data corruption; item with empty name."); - const Zstring& itemPath = appendSeparator(dirPath) + itemNameRaw; + const Zstring& itemName = itemNameRaw; + const Zstring& itemPath = appendSeparator(dirPath) + itemName; if (zen::isSymlink(findData)) //check first! { - if (onLink) - onLink({ itemPath, filetimeToTimeT(findData.ftLastWriteTime) }); + if (onSymlink) + onSymlink({ itemName, itemPath, filetimeToTimeT(findData.ftLastWriteTime) }); } else if ((findData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0) { - if (onDir) - onDir({ itemPath }); + if (onFolder) + onFolder({ itemName, itemPath }); } else //a file { if (onFile) - onFile({ itemPath, get64BitUInt(findData.nFileSizeLow, findData.nFileSizeHigh), filetimeToTimeT(findData.ftLastWriteTime) }); + onFile({ itemName, itemPath, get64BitUInt(findData.nFileSizeLow, findData.nFileSizeHigh), filetimeToTimeT(findData.ftLastWriteTime) }); } } @@ -159,18 +163,18 @@ void zen::traverseFolder(const Zstring& dirPath, if (S_ISLNK(statData.st_mode)) //on Linux there is no distinction between file and directory symlinks! { - if (onLink) - onLink({ itemPath, statData.st_mtime}); + if (onSymlink) + onSymlink({ itemName, itemPath, statData.st_mtime}); } else if (S_ISDIR(statData.st_mode)) //a directory { - if (onDir) - onDir({ itemPath }); + if (onFolder) + onFolder({ itemName, itemPath }); } else //a file or named pipe, ect. { if (onFile) - onFile({ itemPath, makeUnsigned(statData.st_size), statData.st_mtime }); + onFile({ itemName, itemPath, makeUnsigned(statData.st_size), statData.st_mtime }); } /* It may be a good idea to not check "S_ISREG(statData.st_mode)" explicitly and to not issue an error message on other types to support these scenarios: diff --git a/zen/file_traverser.h b/zen/file_traverser.h index 5fffe0e2..1badddf9 100644 --- a/zen/file_traverser.h +++ b/zen/file_traverser.h @@ -16,19 +16,22 @@ namespace zen { struct FileInfo { - const Zstring& fullPath; + Zstring itemName; + Zstring fullPath; std::uint64_t fileSize; //[bytes] std::int64_t lastWriteTime; //number of seconds since Jan. 1st 1970 UTC }; -struct DirInfo +struct FolderInfo { - const Zstring& fullPath; + Zstring itemName; + Zstring fullPath; }; struct SymlinkInfo { - const Zstring& fullPath; + Zstring itemName; + Zstring fullPath; std::int64_t lastWriteTime; //number of seconds since Jan. 1st 1970 UTC }; @@ -36,8 +39,8 @@ struct SymlinkInfo //- directory path may end with PATH_SEPARATOR void traverseFolder(const Zstring& dirPath, //noexcept const std::function<void (const FileInfo& fi)>& onFile, // - const std::function<void (const DirInfo& di)>& onDir, //optional - const std::function<void (const SymlinkInfo& si)>& onLink, // + const std::function<void (const FolderInfo& fi)>& onFolder, //optional + const std::function<void (const SymlinkInfo& si)>& onSymlink, // const std::function<void (const std::wstring& errorMsg)>& onError); // } diff --git a/zen/fixed_list.h b/zen/fixed_list.h index 81197eb4..27eb488c 100644 --- a/zen/fixed_list.h +++ b/zen/fixed_list.h @@ -139,8 +139,8 @@ public: void swap(FixedList& other) { std::swap(firstInsert_, other.firstInsert_); - std::swap(lastInsert_ , other.lastInsert_); - std::swap(sz_ , other.sz_); + std::swap(lastInsert_, other.lastInsert_); + std::swap(sz_, other.sz_); } private: @@ -194,7 +194,7 @@ public: }; using value_type = T; - using iterator = FixedIterator<typename std::vector<std::unique_ptr<T>>::iterator , T>; + using iterator = FixedIterator<typename std::vector<std::unique_ptr<T>>::iterator, T>; using const_iterator = FixedIterator<typename std::vector<std::unique_ptr<T>>::const_iterator, const T>; using reference = T&; using const_reference = const T&; diff --git a/zen/recycler.cpp b/zen/recycler.cpp index 7f9e0a01..d8ee58c4 100644 --- a/zen/recycler.cpp +++ b/zen/recycler.cpp @@ -29,9 +29,8 @@ using namespace zen; #ifdef ZEN_WIN -void zen::recycleOrDelete(const std::vector<Zstring>& itempaths, const std::function<void (const std::wstring& displayPath)>& onRecycleItem) +void zen::recycleOrDeleteIfExists(const std::vector<Zstring>& itemPaths, const std::function<void (const std::wstring& displayPath)>& onRecycleItem) { - if (itempaths.empty()) return; //warning: moving long file paths to recycler does not work! //both ::SHFileOperation() and ::IFileOperation cannot delete a folder named "System Volume Information" with normal attributes but shamelessly report success //both ::SHFileOperation() and ::IFileOperation can't handle \\?\-prefix! @@ -48,21 +47,25 @@ void zen::recycleOrDelete(const std::vector<Zstring>& itempaths, const std::func Nevertheless, let's use IFileOperation for better error reporting (including details on locked files)! */ -#ifdef ZEN_WIN_VISTA_AND_LATER - vista::moveToRecycleBin(itempaths, onRecycleItem); //throw FileError + +#ifdef ZEN_WIN_VISTA_AND_LATER //Win Vista recycle bin usage: 1. good error reporting 2. late failure + vista::moveToRecycleBinIfExists(itemPaths, onRecycleItem); //throw FileError #else //regular recycle bin usage: available since XP: 1. bad error reporting 2. early failure - Zstring itempathsDoubleNull; - for (const Zstring& itempath : itempaths) + //TODO: this XP version fails if any item is not existing violating this function's API + if (itemPaths.empty()) return; + + Zstring itemPathsDoubleNull; + for (const Zstring& itemPath : itemPaths) { - itempathsDoubleNull += itempath; - itempathsDoubleNull += L'\0'; + itemPathsDoubleNull += itemPath; + itemPathsDoubleNull += L'\0'; } SHFILEOPSTRUCT fileOp = {}; fileOp.hwnd = nullptr; fileOp.wFunc = FO_DELETE; - fileOp.pFrom = itempathsDoubleNull.c_str(); + fileOp.pFrom = itemPathsDoubleNull.c_str(); fileOp.pTo = nullptr; fileOp.fFlags = FOF_ALLOWUNDO | FOF_SILENT | FOF_NOCONFIRMATION | FOF_NOERRORUI; fileOp.fAnyOperationsAborted = false; @@ -70,10 +73,10 @@ void zen::recycleOrDelete(const std::vector<Zstring>& itempaths, const std::func fileOp.lpszProgressTitle = nullptr; //"You should use fully-qualified path names with this function. Using it with relative path names is not thread safe." - if (::SHFileOperation(&fileOp) != 0 || fileOp.fAnyOperationsAborted) + if (::SHFileOperation(&fileOp) != 0 || fileOp.fAnyOperationsAborted) //fails if items are not existing! { - std::wstring itempathFmt = fmtPath(itempaths[0]); //probably not the correct file name for file lists larger than 1! - if (itempaths.size() > 1) + std::wstring itempathFmt = fmtPath(itemPaths[0]); //probably not the correct file name for file lists larger than 1! + if (itemPaths.size() > 1) itempathFmt += L", ..."; //give at least some hint that there are multiple files, and the error need not be related to the first one throw FileError(replaceCpy(_("Unable to move %x to the recycle bin."), L"%x", itempathFmt)); } @@ -82,16 +85,37 @@ void zen::recycleOrDelete(const std::vector<Zstring>& itempaths, const std::func #endif -bool zen::recycleOrDelete(const Zstring& itempath) //throw FileError +bool zen::recycleOrDeleteIfExists(const Zstring& itemPath) //throw FileError { - if (!somethingExists(itempath)) //[!] do not optimize away, OS X needs this for reliable detection of "recycle bin missing" - return false; //neither file nor any other object with that name existing: no error situation, manual deletion relies on it! - #ifdef ZEN_WIN - recycleOrDelete({ itempath }, nullptr); //throw FileError +#ifdef ZEN_WIN_VISTA_AND_LATER + return vista::moveToRecycleBinIfExists({ itemPath }, nullptr) != 0; //throw FileError + +#else + Zstring itemPathDoubleNull = itemPath; + itemPathDoubleNull += L'\0'; + + SHFILEOPSTRUCT fileOp = {}; + fileOp.wFunc = FO_DELETE; + fileOp.pFrom = itemPathDoubleNull.c_str(); + fileOp.fFlags = FOF_ALLOWUNDO | FOF_SILENT | FOF_NOCONFIRMATION | FOF_NOERRORUI; + + if (::SHFileOperation(&fileOp) != 0 || fileOp.fAnyOperationsAborted) //fails if item is not existing! + { + try + { + if (!getItemTypeIfExists(itemPath)) //throw FileError + return false; + } + catch (FileError&) {} //previous exception is more relevant + + throw FileError(replaceCpy(_("Unable to move %x to the recycle bin."), L"%x", fmtPath(itemPath))); + } + return true; +#endif #elif defined ZEN_LINUX - GFile* file = ::g_file_new_for_path(itempath.c_str()); //never fails according to docu + GFile* file = ::g_file_new_for_path(itemPath.c_str()); //never fails according to docu ZEN_ON_SCOPE_EXIT(g_object_unref(file);) GError* error = nullptr; @@ -99,38 +123,38 @@ bool zen::recycleOrDelete(const Zstring& itempath) //throw FileError if (!::g_file_trash(file, nullptr, &error)) { - const std::wstring errorMsg = replaceCpy(_("Unable to move %x to the recycle bin."), L"%x", fmtPath(itempath)); + const Opt<ItemType> type = getItemTypeIfExists(itemPath); //throw FileError + if (!type) + return false; + const std::wstring errorMsg = replaceCpy(_("Unable to move %x to the recycle bin."), L"%x", fmtPath(itemPath)); if (!error) throw FileError(errorMsg, L"g_file_trash: unknown error."); //user should never see this //implement same behavior as in Windows: if recycler is not existing, delete permanently if (error->code == G_IO_ERROR_NOT_SUPPORTED) { - struct ::stat fileInfo = {}; - if (::lstat(itempath.c_str(), &fileInfo) != 0) - return false; - - if (S_ISLNK(fileInfo.st_mode) || S_ISREG(fileInfo.st_mode)) - removeFile(itempath); //throw FileError - else if (S_ISDIR(fileInfo.st_mode)) - removeDirectoryRecursively(itempath); //throw FileError + if (*type == ItemType::FOLDER) + removeDirectoryPlainRecursion(itemPath); //throw FileError + else + removeFilePlain(itemPath); //throw FileError return true; } throw FileError(errorMsg, replaceCpy<std::wstring>(L"Glib Error Code %x:", L"%x", numberTo<std::wstring>(error->code)) + L" " + utfCvrtTo<std::wstring>(error->message)); //g_quark_to_string(error->domain) } + return true; #elif defined ZEN_MAC //we cannot use FSPathMoveObjectToTrashSync directly since it follows symlinks! static_assert(sizeof(Zchar) == sizeof(char), ""); - const UInt8* itempathUtf8 = reinterpret_cast<const UInt8*>(itempath.c_str()); + const UInt8* itemPathUtf8 = reinterpret_cast<const UInt8*>(itemPath.c_str()); auto throwFileError = [&](OSStatus oss) { - const std::wstring errorMsg = replaceCpy(_("Unable to move %x to the recycle bin."), L"%x", fmtPath(itempath)); + const std::wstring errorMsg = replaceCpy(_("Unable to move %x to the recycle bin."), L"%x", fmtPath(itemPath)); std::wstring errorDescr = L"OSStatus Code " + numberTo<std::wstring>(oss); if (const char* description = ::GetMacOSStatusCommentString(oss)) //found no documentation for proper use of GetMacOSStatusCommentString @@ -138,8 +162,14 @@ bool zen::recycleOrDelete(const Zstring& itempath) //throw FileError throw FileError(errorMsg, errorDescr); }; + //[!] do not optimize away, OS X needs this for reliable detection of "recycle bin unsupported" + //both "not supported" and "item missing" let FSMoveObjectToTrashSync fail with -120 + const Opt<ItemType> type = getItemTypeIfExists(itemPath); //throw FileError + if (!type) + return false; + FSRef objectRef = {}; //= POD structure not a pointer type! - OSStatus rv = ::FSPathMakeRefWithOptions(itempathUtf8, //const UInt8 *path, + OSStatus rv = ::FSPathMakeRefWithOptions(itemPathUtf8, //const UInt8 *path, kFSPathMakeRefDoNotFollowLeafSymlink, //OptionBits options, &objectRef, //FSRef *ref, nullptr); //Boolean *isDirectory @@ -155,21 +185,17 @@ bool zen::recycleOrDelete(const Zstring& itempath) //throw FileError //implement same behavior as in Windows: if recycler is not existing, delete permanently if (rv2 == -120) //=="Directory not found or incomplete pathname." but should really be "recycle bin directory not found"! { - struct ::stat fileInfo = {}; - if (::lstat(itempath.c_str(), &fileInfo) != 0) - return false; - - if (S_ISLNK(fileInfo.st_mode) || S_ISREG(fileInfo.st_mode)) - removeFile(itempath); //throw FileError - else if (S_ISDIR(fileInfo.st_mode)) - removeDirectoryRecursively(itempath); //throw FileError + if (*type == ItemType::FOLDER) + removeDirectoryPlainRecursion(itemPath); //throw FileError + else + removeFilePlain(itemPath); //throw FileError return true; } throwFileError(rv2); } -#endif return true; +#endif } diff --git a/zen/recycler.h b/zen/recycler.h index 4a5f4b2b..ec2a8672 100644 --- a/zen/recycler.h +++ b/zen/recycler.h @@ -35,7 +35,7 @@ Already included in package "gtk+-2.0"! //move a file or folder to Recycle Bin (deletes permanently if recycler is not available) -> crappy semantics, but we have no choice thanks to Windows' design -bool recycleOrDelete(const Zstring& itemPath); //throw FileError, return "true" if file/dir was actually deleted +bool recycleOrDeleteIfExists(const Zstring& itemPath); //throw FileError, return "true" if file/dir was actually deleted #ifdef ZEN_WIN @@ -43,8 +43,8 @@ bool recycleOrDelete(const Zstring& itemPath); //throw FileError, return "true" //Vista and later: dirPath must exist for a valid check! bool recycleBinExists(const Zstring& dirPath, const std::function<void ()>& onUpdateGui); //throw FileError -void recycleOrDelete(const std::vector<Zstring>& filePaths, //throw FileError, return "true" if file/dir was actually deleted - const std::function<void (const std::wstring& displayPath)>& onRecycleItem); //optional; currentItem may be empty +void recycleOrDeleteIfExists(const std::vector<Zstring>& filePaths, //throw FileError + const std::function<void (const std::wstring& displayPath)>& onRecycleItem); //optional; currentItem may be empty #endif } diff --git a/zen/serialize.h b/zen/serialize.h index 7322cb07..290d9200 100644 --- a/zen/serialize.h +++ b/zen/serialize.h @@ -126,7 +126,7 @@ struct MemoryStreamIn return bytesRead; } - size_t pos() const { return pos_; } + size_t pos() const { return pos_; } private: const BinContainer buffer_; diff --git a/zen/shell_execute.h b/zen/shell_execute.h index 2f73fc38..b5e04469 100644 --- a/zen/shell_execute.h +++ b/zen/shell_execute.h @@ -68,7 +68,7 @@ void shellExecute(const void* /*PCIDLIST_ABSOLUTE*/ shellItemPidl, const std::ws execInfo.lpIDList = const_cast<void*>(shellItemPidl); //lpIDList is documented as PCIDLIST_ABSOLUTE! }; - if (!shellExecuteImpl(fillExecInfo , type)) //throw FileError + if (!shellExecuteImpl(fillExecInfo, type)) //throw FileError THROW_LAST_FILE_ERROR(_("Incorrect command line:") + L"\n" + fmtPath(displayPath), L"ShellExecuteEx"); } #endif diff --git a/zen/thread.h b/zen/thread.h index 5bb02a0e..f6c3ae01 100644 --- a/zen/thread.h +++ b/zen/thread.h @@ -398,8 +398,8 @@ InterruptibleThread::InterruptibleThread(Function&& f) : intStatus_(std::make_sh threadCompleted = pFinished.get_future(); stdThread = std::thread([f = std::forward<Function>(f), - intStatus = this->intStatus_, - pFinished = std::move(pFinished)]() mutable + intStatus = this->intStatus_, + pFinished = std::move(pFinished)]() mutable { assert(!impl::refThreadLocalInterruptionStatus()); impl::refThreadLocalInterruptionStatus() = intStatus.get(); |