bool CState::RecursiveCopy(CLocalPath source, const CLocalPath& target) { if (source.empty() || target.empty()) return false; if (source == target) return false; if (source.IsParentOf(target)) return false; if (!source.HasParent()) return false; wxString last_segment; if (!source.MakeParent(&last_segment)) return false; std::list<wxString> dirsToVisit; dirsToVisit.push_back(last_segment + CLocalPath::path_separator); // Process any subdirs which still have to be visited while (!dirsToVisit.empty()) { wxString dirname = dirsToVisit.front(); dirsToVisit.pop_front(); wxMkdir(target.GetPath() + dirname); CLocalFileSystem fs; if (!fs.BeginFindFiles(source.GetPath() + dirname, false)) continue; bool is_dir, is_link; wxString file; while (fs.GetNextFile(file, is_link, is_dir, 0, 0, 0)) { if (file.empty()) { wxGetApp().DisplayEncodingWarning(); continue; } if (is_dir) { if (is_link) continue; const wxString subDir = dirname + file + CLocalPath::path_separator; dirsToVisit.push_back(subDir); } else wxCopyFile(source.GetPath() + dirname + file, target.GetPath() + dirname + file); } } return true; }
bool CLocalFileSystem::RecursiveDelete(std::list<wxString> dirsToVisit, wxWindow* parent) { // Under Windows use SHFileOperation to delete files and directories. // Under other systems, we have to recurse into subdirectories manually // to delete all contents. #ifdef __WXMSW__ // SHFileOperation accepts a list of null-terminated strings. Go through all // paths to get the required buffer length size_t len = 1; // String list terminated by empty string for (auto const& dir : dirsToVisit) { len += dir.size() + 1; } // Allocate memory wxChar* pBuffer = new wxChar[len]; wxChar* p = pBuffer; for (auto& dir : dirsToVisit) { if (dir.Last() == wxFileName::GetPathSeparator()) dir.RemoveLast(); if (GetFileType(dir) == unknown) continue; _tcscpy(p, dir); p += dir.size() + 1; } if (p != pBuffer) { *p = 0; // Now we can delete the files in the buffer SHFILEOPSTRUCT op; memset(&op, 0, sizeof(op)); op.hwnd = parent ? (HWND)parent->GetHandle() : 0; op.wFunc = FO_DELETE; op.pFrom = pBuffer; if (parent) { // Move to trash if shift is not pressed, else delete op.fFlags = wxGetKeyState(WXK_SHIFT) ? 0 : FOF_ALLOWUNDO; } else op.fFlags = FOF_NOCONFIRMATION; SHFileOperation(&op); } delete [] pBuffer; return true; #else if (parent) { if (wxMessageBoxEx(_("Really delete all selected files and/or directories from your computer?"), _("Confirmation needed"), wxICON_QUESTION | wxYES_NO, parent) != wxYES) return true; } for (auto& dir : dirsToVisit) { if (dir.Last() == '/' && dir != _T("/")) dir.RemoveLast(); } bool encodingError = false; // Remember the directories to delete after recursing into them std::list<wxString> dirsToDelete; CLocalFileSystem fs; // Process all dirctories that have to be visited while (!dirsToVisit.empty()) { auto const iter = dirsToVisit.begin(); wxString const& path = *iter; if (GetFileType(path) != dir) { wxRemoveFile(path); dirsToVisit.erase(iter); continue; } dirsToDelete.splice(dirsToDelete.begin(), dirsToVisit, iter); if (!fs.BeginFindFiles(path, false)) { continue; } // Depending on underlying platform, wxDir does not handle // changes to the directory contents very well. // See http://trac.filezilla-project.org/ticket/3482 // To work around this, delete files after enumerating everything in current directory std::list<wxString> filesToDelete; wxString file; while (fs.GetNextFile(file)) { if (file.empty()) { encodingError = true; continue; } const wxString& fullName = path + _T("/") + file; if (CLocalFileSystem::GetFileType(fullName) == CLocalFileSystem::dir) dirsToVisit.push_back(fullName); else filesToDelete.push_back(fullName); } fs.EndFindFiles(); // Delete all files and links in current directory enumerated before for (auto const& file : filesToDelete) { wxRemoveFile(file); } } // Delete the now empty directories for (auto const& dir : dirsToDelete) { wxRmdir(dir); } return !encodingError; #endif }