/* ** This program is free software; you can redistribute it and/or modify ** it under the terms of the GNU General Public License as published by ** the Free Software Foundation; either version 1, or (at your option) ** any later version. ** This program is distributed in the hope that it will be useful, ** but WITHOUT ANY WARRANTY; without even the implied warranty of ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ** GNU General Public License for more details. ** You should have received a copy of the GNU General Public License ** along with this program; if not, write to the Free Software ** Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ /* * MoveToTrash.cpp --- send the files to the trash * Author : Miro Jurisic --- December 1997 * Modified : Alexandre Parenteau --- December 1997 */ #include "stdafx.h" #ifdef macintosh # include # include # include # include # include #endif /* macintosh */ #ifdef qUnix # include #endif /* qUnix */ #ifdef HAVE_UNISTD_H # include #endif #include "MoveToTrash.h" #include "CPStr.h" #include "FileTraversal.h" #include "AppConsole.h" #ifdef WIN32 # include #endif /* WIN32 */ #ifdef WIN32 # ifdef _DEBUG # define new DEBUG_NEW # undef THIS_FILE static char THIS_FILE[] = __FILE__; # endif #endif /* WIN32 */ #ifdef macintosh static OSErr GetAddressOfFinder ( AEAddressDesc* outFinderAddress); static OSErr SendFilesToTrash ( AEAddressDesc const* inFinderAddress, short inFileCount, const UFSSpec* inFileList); static OSErr PutFilesInAE ( short inFileCount, const UFSSpec* inFileList, AppleEvent * ioAppleEvent); static OSErr ToolboxMoveToTrash(const UFSSpec* spec); const OSType kFinderSignature = 'MACS'; static std::vector sToTrash; static bool sTrashRetainer = false; static OSErr MoveToTrash (const std::vector & inFiles) { OSErr err = noErr; AEDesc finderAddress = {typeNull, nil}; /* First unlock the file so that the Finder can rename it if there are already files of that name in the Trash */ std::vector::const_iterator i; for(i = inFiles.begin(); i != inFiles.end(); ++i) { #if TARGET_RT_MAC_CFM err = FSpRstFLock (&*i); #else FSCatalogInfo info; if((err = FSGetCatalogInfo(&*i, kFSCatInfoNodeFlags, &info, NULL, NULL, NULL)) == noErr && (info.nodeFlags & kFSNodeLockedMask) != 0) { info.nodeFlags = info.nodeFlags & ~kFSNodeLockedMask; err = FSSetCatalogInfo(&*i, kFSCatInfoNodeFlags, &info); } #endif if (err != noErr) goto cleanup; } err = GetAddressOfFinder (&finderAddress); if (err != noErr) goto cleanup; err = SendFilesToTrash (&finderAddress, inFiles.size(), &inFiles[0]); if (err != noErr) goto cleanup; cleanup: if (finderAddress.dataHandle != nil) { AEDisposeDesc (&finderAddress); } if(err != noErr) { /* try the toolbox way */ for(i = inFiles.begin(); i != inFiles.end(); ++i) { err = ToolboxMoveToTrash(&*i); } } return err; } static OSErr MoveToTrash (const UFSSpec* inFile) { OSErr err = noErr; AEDesc finderAddress = {typeNull, nil}; /* First unlock the file so that the Finder can rename it if there are already files of that name in the Trash */ #if TARGET_RT_MAC_CFM err = FSpRstFLock (inFile); #else FSCatalogInfo info; if((err = FSGetCatalogInfo(inFile, kFSCatInfoNodeFlags, &info, NULL, NULL, NULL)) == noErr && (info.nodeFlags & kFSNodeLockedMask) != 0) { info.nodeFlags = info.nodeFlags & ~kFSNodeLockedMask; err = FSSetCatalogInfo(inFile, kFSCatInfoNodeFlags, &info); } #endif if (err != noErr) goto cleanup; err = GetAddressOfFinder (&finderAddress); if (err != noErr) goto cleanup; err = SendFilesToTrash (&finderAddress, 1, inFile); if (err != noErr) goto cleanup; cleanup: if (finderAddress.dataHandle != nil) { AEDisposeDesc (&finderAddress); } if(err != noErr) { /* try the toolbox way */ err = ToolboxMoveToTrash(inFile); } return err; } static OSErr ToolboxMoveToTrash(const UFSSpec* spec) { OSErr err; #if TARGET_RT_MAC_CFM short foundVRefNum; long foundDirID; err = FindFolder(spec->vRefNum, kTrashFolderType, kDontCreateFolder, &foundVRefNum, &foundDirID); if(err != noErr) return err; err = CatMove(spec->vRefNum, spec->parID, spec->name, foundDirID, "\p:"/*spec.name*/); #else FSRef folder; err = FSFindFolder(kOnAppropriateDisk, kTrashFolderType, kDontCreateFolder, &folder); if(err != noErr) return err; err = FSMoveObject(spec, &folder, NULL); #endif return err; } /* get address of the Finder (to use as the destination of our appleevents) */ static OSErr GetAddressOfFinder ( AEAddressDesc* outFinderAddress) { return (AECreateDesc (typeApplSignature, (Ptr) &kFinderSignature, sizeof (long), outFinderAddress)); } /* Tell Finder to send a list of files to the trash */ static OSErr SendFilesToTrash ( AEAddressDesc const* inFinderAddress, short inFileCount, const UFSSpec* inFileList) { OSErr err = noErr; AppleEvent theAppleEvent = {typeNull, nil}; AppleEvent theReply = {typeNull, nil}; DescType trashType = kTrashFolderType; AEDesc keyData = {typeNull, nil}; AEDesc trashSpecifier = {typeNull, nil}; AEDesc nullDescriptor = {typeNull, nil}; /* create the appleevent */ err = AECreateAppleEvent (kAECoreSuite, kAEMove, inFinderAddress, kAutoGenerateReturnID, kAnyTransactionID, &theAppleEvent); if (err != noErr) goto cleanup; /* put the files into the direct object of the appleevent */ err = PutFilesInAE (inFileCount, inFileList, &theAppleEvent); if (err != noErr) goto cleanup; /* create a descriptor for trash */ err = AECreateDesc (typeType, (Ptr) &trashType, sizeof (DescType) , &keyData); if (err != noErr) goto cleanup; /* get specifier for trash object */ err = CreateObjSpecifier (typeProperty, &nullDescriptor, formPropertyID, &keyData, true, &trashSpecifier); if (err != noErr) goto cleanup; /* put the trash specifier as the indirect object in the appleevent */ err = AEPutParamDesc (&theAppleEvent, keyAEInsertHere, &trashSpecifier); if (err != noErr) goto cleanup; /* send the appleevent */ err = AESend (&theAppleEvent, &theReply, kAEWaitReply, kAENormalPriority, kAEDefaultTimeout, nil, nil); if (err != noErr) goto cleanup; /* ignore the reply */ cleanup: /* dispose of the stuff that we allocated */ if (theAppleEvent.dataHandle != nil) { AEDisposeDesc (&theAppleEvent); } if (theReply.dataHandle != nil) { AEDisposeDesc (&theReply); } if (keyData.dataHandle != nil) { AEDisposeDesc (&keyData); } if (trashSpecifier.dataHandle != nil) { AEDisposeDesc (&trashSpecifier); } /* don't have to dispose of nullDescriptor because it's null */ return err; } /* put a list of files into the direct object of an apple event */ static OSErr PutFilesInAE ( short inFileCount, const UFSSpec* inFileList, AppleEvent* ioAppleEvent) { OSErr err; AEDescList fileList = {typeNull, nil}; short i; #if TARGET_RT_MAC_MACHO AliasHandle alias; #endif err = AECreateList(nil, 0, false, &fileList); if (err != noErr) return err; for (i = 1; i <= inFileCount; i++) { #if TARGET_RT_MAC_CFM err = AEPutPtr(&fileList, i, typeFSS, (Ptr) (inFileList + i - 1), sizeof(UFSSpec)); #else err = ::FSNewAlias(NULL, inFileList + i - 1, &alias); if (err != noErr) goto cleanup; ::HLock((Handle)alias); err = AEPutPtr(&fileList, i, typeAlias, *alias, ::GetHandleSize((Handle)alias)); ::HUnlock((Handle)alias); ::DisposeHandle((Handle)alias); #endif if (err != noErr) goto cleanup; } err = AEPutParamDesc(ioAppleEvent, keyDirectObject, &fileList); cleanup: if (fileList.dataHandle != nil) { AEDisposeDesc(&fileList); } return err; } #endif /* macintosh */ void CompatBeginMoveToTrash(void) { #ifdef macintosh sToTrash.erase(sToTrash.begin(), sToTrash.end()); sTrashRetainer = true; #endif } void CompatEndMoveToTrash(void) { #ifdef macintosh if(sToTrash.size() > 0) { OSErr err; err = MoveToTrash(sToTrash); if(err != noErr) cvs_err("Unable to send files to the trash (Error %d)\n", err); else cvs_out("Files has been moved successfully to the trash...\n"); } sToTrash.erase(sToTrash.begin(), sToTrash.end()); sTrashRetainer = false; #endif } bool CompatMoveToTrash(const char *file, const char *dir, const UFSSpec *spec) { #ifdef macintosh if(sTrashRetainer) { sToTrash.push_back(*spec); return true; } return spec != NULL ? MoveToTrash(spec) == noErr : false; #elif defined(WIN32) SHFILEOPSTRUCT fileop; CStr fullpath(dir); if(!fullpath.empty() && !fullpath.endsWith(kPathDelimiter)) { fullpath << kPathDelimiter; } fullpath << file; fullpath << ' '; fullpath[fullpath.length() - 1] = '\0'; fileop.hwnd = *AfxGetMainWnd(); fileop.wFunc = FO_DELETE; fileop.pFrom = fullpath; fileop.pTo = 0L; fileop.fFlags = FOF_ALLOWUNDO | FOF_SILENT | FOF_NOCONFIRMATION | FOF_NOERRORUI; if(SHFileOperation(&fileop) != 0) { if(dir != 0L && chdir(dir) != 0) return false; cvs_err("Unable to send \'%s\' to the recycle bin (error %d)\n", file, GetLastError()); cvs_err("Trying now to delete \'%s\' instead\n", file); return remove(file) == 0; } return true; #else /* !macintosh && !WIN32 */ CStr fullpath(dir); if(!fullpath.empty() && !fullpath.endsWith(kPathDelimiter)) { fullpath << kPathDelimiter; } fullpath << file; return remove(fullpath) == 0; #endif /* !macintosh && !WIN32 */ }