diff options
Diffstat (limited to 'zen/recycler.cpp')
-rw-r--r-- | zen/recycler.cpp | 104 |
1 files changed, 65 insertions, 39 deletions
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 } |