FileOp.cpp
上传用户:hy_wanghao
上传日期:2007-01-08
资源大小:279k
文件大小:16k
- // FileOp.cpp : Implementation of CFolder's file operation functions
- #include "stdafx.h"
- #include "Folder.h"
- #include "Misc.h"
- #include "DuplicateDlg.h"
- /////////////////////////////////////////////////////////////////////////////
- // CFolder
- // Macro checking if a FILEOP flags is present
- // Since the 'pdwFileOpFlags' argument is optional, we need to check
- // if the pointer is valid first.
- #define FILEOP_FLAG(x) ((pdwFileOpFlags!=NULL) && ((*pdwFileOpFlags & x)!=0))
- // Opens an Amiga disk
- HRESULT CFolder::_OpenAmigaDevice(LPCITEMIDLIST pidlPath,
- BOOL bReadOnly,
- LPCITEMIDLIST pidlStartFolder,
- CAdfDevice &dev,
- CAdfVolume &vol)
- {
- ATLASSERT(pidlPath);
- ATLASSERT(!dev.IsOpen());
- ATLASSERT(!vol.IsOpen());
- CShellPidlPath sDevPath(pidlPath);
- return ::OpenAmigaDevice(sDevPath, bReadOnly, pidlStartFolder, dev, vol);
- }
- // Creates a new folder on the Amiga volume.
- // Note that the 'pstrDestName' argument may be modified.
- HRESULT CFolder::_CreateFolder(LPTSTR pstrDestName, LPDWORD pdwFileOpFlags)
- {
- ATLASSERT(pstrDestName);
- HRESULT Hr;
- CAdfDevice dev;
- CAdfVolume vol;
- HR( _OpenAmigaDevice(m_pidl, FALSE, m_pidlPath, dev, vol) );
- // Make sure it's a valid pathname
- if( _ValidateFileName(pstrDestName, pdwFileOpFlags)==FALSE ) return E_FAIL;
- // If we're asked to find a unique folder name, do so...
- if( FILEOP_FLAG(FILEOP_UNIQUENAME) ) {
- if( _FindUniqueFileName(vol, pstrDestName)==FALSE ) return E_FAIL;
- }
- // Check for duplicates
- BOOL bIsDuplicate;
- if( _CheckFileCollision(vol, pstrDestName, pdwFileOpFlags, &bIsDuplicate )!=IDOK ) return S_OK;
- if( bIsDuplicate ) return S_OK; // It's already there. Don't recreate it.
- // Check that there is at least one disk block free for our new folder
- // NOTE: The argument is 1 byte, but it gets translated into 512 bytes (1 block)
- // by the conversion function.
- if( _CheckForDiskExchaustion(vol, 1, pdwFileOpFlags)==FALSE ) return E_UNEXPECTED;
- // Go create the directory then...
- if( vol.CreateDirectory(vol.GetDirectoryPtr(), pstrDestName)==FALSE ) return E_FAIL;
- return S_OK;
- }
- // Deletes a list of files and folders from the current folder.
- HRESULT CFolder::_DeleteFiles(LPCITEMIDLIST *pidls, UINT nCount)
- {
- ATLASSERT(pidls);
- ATLASSERT(nCount>0);
- if( pidls==NULL || nCount==0 ) return E_INVALIDARG;
- HRESULT Hr;
- CAdfDevice dev;
- CAdfVolume vol;
- HR( _OpenAmigaDevice(m_pidl, FALSE, m_pidlPath, dev, vol) );
- // For all PIDL items, delete them...
- for( UINT i=0; i<nCount; i++ ) {
-
- DWORD dwAttr = SFGAO_CANDELETE;
- GetAttributesOf(1, &pidls[i], &dwAttr);
- if( (dwAttr & SFGAO_CANDELETE)==0 ) continue;
- TCHAR szFileName[MAXNAMELEN+1];
- PidlGetName(szFileName, pidls[i]);
- bool bIsFolder = ( PidlGetType(pidls[i])==PT_FOLDER );
- bool bDeleted;
-
- if( bIsFolder ) {
- bDeleted = ( _RemoveDirectory(vol, szFileName)==S_OK );
- }
- else {
- bDeleted = ( vol.Delete(vol.GetDirectoryPtr(), szFileName)==TRUE );
- }
- if( bDeleted ) {
- // Notify Shell so it can remove the folder in the Explorer tree
- CPidl pidl;
- pidl.Copy(m_pidl);
- pidl.Concatenate(m_pidlPath);
- pidl.Concatenate(pidls[i]);
- ::SHChangeNotify(bIsFolder ? SHCNE_RMDIR : SHCNE_DELETE, SHCNF_IDLIST | SHCNF_FLUSH, pidl, NULL);
- }
- }
- // Ask Shell to refresh current directory
- CPidl pidl;
- pidl.Copy(m_pidl);
- pidl.Concatenate(m_pidlPath);
- ::SHChangeNotify(SHCNE_UPDATEDIR, SHCNF_IDLIST | SHCNF_FLUSH, pidl, NULL);
- return S_OK;
- }
- // Renames a file or a folder.
- HRESULT CFolder::_RenameFile(LPCITEMIDLIST pidlOld, LPCITEMIDLIST pidlNew)
- {
- ATLASSERT(pidlOld);
- ATLASSERT(pidlNew);
- CAdfDevice dev;
- CAdfVolume vol;
- HRESULT Hr;
- HR( _OpenAmigaDevice(m_pidl, FALSE, m_pidlPath, dev, vol) );
- TCHAR szNewName[MAXNAMELEN+1];
- PidlGetName(szNewName, pidlNew);
- TCHAR szOldName[MAXNAMELEN+1];
- PidlGetName(szOldName, pidlOld);
- // Check for invalid chars
- DWORD dwFlags = FILEOP_DONTTRUNCATE;
- if( _ValidateFileName(szNewName, &dwFlags)==FALSE ) return E_FAIL;
- // Check for duplicates
- dwFlags = FILEOP_SILENT;
- BOOL bIsDuplicate;
- _CheckFileCollision(vol, szNewName, &dwFlags, &bIsDuplicate);
- if( bIsDuplicate ) return E_FAIL;
- // Ok, go ahead and rename
- BOOL bRes = vol.Rename(vol.GetDirectoryPtr(), szOldName, vol.GetDirectoryPtr(), szNewName);
- if( !bRes ) return E_FAIL;
- // Notify Shell so it can rename the item in the Explorer tree
- CPidl pidlFullOld;
- pidlFullOld.Copy(m_pidl);
- pidlFullOld.Concatenate(m_pidlPath);
- pidlFullOld.Concatenate(pidlOld);
- CPidl pidlFullNew;
- pidlFullNew.Copy(m_pidl);
- pidlFullNew.Concatenate(m_pidlPath);
- pidlFullNew.Concatenate(pidlNew);
- ::SHChangeNotify(
- PidlGetType(pidlOld)==PT_FOLDER ? SHCNE_RENAMEFOLDER : SHCNE_RENAMEITEM,
- SHCNF_IDLIST,
- pidlFullOld, pidlFullNew);
- CPidl pidlFullPath;
- pidlFullPath.Copy(m_pidl);
- pidlFullPath.Concatenate(m_pidlPath);
- ::SHChangeNotify(SHCNE_UPDATEDIR, SHCNF_IDLIST, pidlFullPath, NULL);
- return S_OK;
- }
- // UnDeletes a list of files and folders from the current folder.
- HRESULT CFolder::_UnDeleteFiles(LPCITEMIDLIST *pidls, UINT nCount)
- {
- ATLASSERT(pidls);
- ATLASSERT(nCount>0);
- HRESULT Hr;
- CAdfDevice dev;
- CAdfVolume vol;
- HR( _OpenAmigaDevice(m_pidl, FALSE, m_pidlPath, dev, vol) );
- CAdfDelList list;
- if( vol.GetDeletedEntries(list)==FALSE ) return E_FAIL;
- List *cell = list;
- while( cell!=NULL ) {
- GenBlock *entry = (GenBlock *)cell->content;
- cell = cell->next;
- if( entry->parent != vol.GetDirectoryPtr() ) continue;
- for( UINT i=0; i<nCount; i++ ) {
- if( _tcscmp(entry->name, (pidl_cast(pidls[0]))->szName)==0 ) {
- if( vol.CheckDeletedFile(entry->sect) ) vol.UndeleteFile(entry->parent, entry->sect);
- }
- }
- }
- // Ask Shell to refresh current directory
- CPidl pidl;
- pidl.Copy(m_pidl);
- pidl.Concatenate(m_pidlPath);
- ::SHChangeNotify(SHCNE_UPDATEDIR, SHCNF_IDLIST | SHCNF_FLUSH, pidl, NULL);
- return S_OK;
- }
- // Removes an Amiga folder.
- // The function removes sub-folders too. It is recursive.
- // The Volume must have the directory pointer placed at
- // the parent directory structure of the folder to be deleted.
- HRESULT CFolder::_RemoveDirectory(CAdfVolume &vol, LPCTSTR pstrPath)
- {
- ATLASSERT(pstrPath);
- USES_CONVERSION;
- // Change into the folder, and delete items recursively...
- if( vol.ChangeDirectory(pstrPath)==FALSE ) return E_UNEXPECTED;
- CAdfDirList dir;
- if( vol.GetCurrentDirctory(dir)==FALSE ) return E_FAIL;
- List *list = dir;
- while (list) {
- struct Entry *ent = (struct Entry *)list->content;
- if( ent->type==ST_DIR ) {
- // It's a dir - recurse into it
- _RemoveDirectory(vol, A2CT(ent->name));
- }
- else {
- // It's a file or a link, just remove it
- vol.Delete(vol.GetDirectoryPtr(), A2CT(ent->name));
- }
- list = list->next;
- }
- // Back to parent and delete the folder...
- vol.ChangeDirectoryParent();
- if( vol.Delete(vol.GetDirectoryPtr(), pstrPath)==FALSE ) return E_FAIL;
- return S_OK;
- }
- // Moves a PC file to the Amiga device
- // Returns S_FALSE if the move operations was cancelled.
- HRESULT CFolder::_MoveFile(LPCTSTR pstrSource, LPCTSTR pstrDestPath, LPCTSTR pstrDestName, LPDWORD pdwFileOpFlags)
- {
- ATLASSERT(pstrSource);
- HRESULT Hr;
- HR( _CopyFile(pstrSource, pstrDestPath, pstrDestName, pdwFileOpFlags) );
- // S_OK means success and we should delete the source file
- // S_FALSE is returned if the copy was cancelled
- if( Hr==S_OK ) {
- ::SetFileAttributes(pstrSource, FILE_ATTRIBUTE_NORMAL);
- ::DeleteFile(pstrSource);
- }
- return Hr;
- }
- // Copies a PC file to the Amiga device
- // If a filename collision occurs, a dialog is shown.
- // Returns S_FALSE if the copy operations was cancelled.
- HRESULT CFolder::_CopyFile(LPCTSTR pstrSource, LPCTSTR pstrDestPath, LPCTSTR pstrDestName, LPDWORD pdwFileOpFlags)
- {
- ATLASSERT(pstrSource);
- ATLASSERT(pstrDestPath);
- ATLASSERT(pstrDestName);
- pstrDestPath;
- if( *pstrSource==_T(' ') ) return E_FAIL;
- if( ::PathFileExists(pstrSource)==FALSE ) return E_INVALIDARG;
- HRESULT Hr;
- CAdfDevice dev;
- CAdfVolume vol;
- HR( _OpenAmigaDevice(m_pidl, FALSE, m_pidlPath, dev, vol) );
- TCHAR szDestName[MAX_PATH];
- _tcscpy( szDestName, pstrDestName );
- if( _ValidateFileName(szDestName, pdwFileOpFlags)==FALSE ) return E_FAIL;
- BOOL bIsDuplicate;
- if( _CheckFileCollision(vol, szDestName, pdwFileOpFlags, &bIsDuplicate )!=IDOK ) return S_FALSE;
- if( bIsDuplicate ) {
- // The file was already there. Need to delete it so we can replace it.
- // BUG: If it was a folder, this will fail.
- vol.Delete(vol.GetDirectoryPtr(), szDestName);
- }
- // Get the file's attributes
- DWORD dwFileAttribs = ::GetFileAttributes(pstrSource);
- // Open the PC file and copy it to the Amiga device
- CFile f;
- if( f.Open(pstrSource)==FALSE ) return E_FAIL;
- // Make sure there is disk space
- if( _CheckForDiskExchaustion(vol, f.GetSize(), pdwFileOpFlags)==FALSE ) return E_UNEXPECTED;
- // Create the new file
- CAdfFile file;
- if( vol.GetFile(szDestName, _T("w"), file)==FALSE ) return E_ACCESSDENIED;
- // Read the buffer and write it to the Amiga file.
- // Note the comma in the 'while' statement.
- // We don't care about FALSE returned from Read()!
- BYTE buf[512];
- DWORD dwRead;
- while( f.Read(buf, sizeof(buf), &dwRead), dwRead>0 ) {
- if( file.Write(buf, dwRead)==FALSE ) return E_UNEXPECTED;
- }
- f.Close();
- file.Close();
- // Set the new file attributes
- DWORD dwAccess = 0;
- if( dwFileAttribs & FILE_ATTRIBUTE_READONLY ) dwAccess |= ACCMASK_W;
- if( dwFileAttribs & FILE_ATTRIBUTE_HIDDEN ) dwAccess |= ACCMASK_H;
- if( dwFileAttribs & FILE_ATTRIBUTE_ARCHIVE ) dwAccess |= ACCMASK_A;
- vol.SetFileAccess(szDestName, vol.GetDirectoryPtr(), dwAccess);
-
- return S_OK;
- }
- // Check for free disk space.
- // The Volume must be opened with read/write access.
- // Will bring up an error dialog and update 'pdwFileOpFlags' argument with
- // FILEOP_CANCEL flag if there is insufficient disk space free.
- BOOL CFolder::_CheckForDiskExchaustion(CAdfVolume &vol, DWORD dwFileSize, LPDWORD pdwFileOpFlags)
- {
- ATLASSERT(vol.IsOpen());
- DWORD dwBlockNeeded = CAdfFile::_CalcBlocksNeeded(dwFileSize, vol.GetDataBlockSize());
- if( vol.GetFreeBlockCount() < dwBlockNeeded ) {;
- if( !FILEOP_FLAG(FILEOP_SILENT) ) {
- // Display "Not enough free space" warning
- CResString<200> sText(IDS_DISKFULL);
- CResString<64> sCaption(IDS_WARNING);
- ::MessageBox(NULL, sText, sCaption, MB_OK | MB_ICONEXCLAMATION | MB_SETFOREGROUND);
- }
- // Make sure to abort operation completely after this
- if( pdwFileOpFlags!=NULL ) *pdwFileOpFlags |= FILEOP_CANCEL;
- return FALSE;
- }
- if( vol.IsReadOnly() ) {
- if( !FILEOP_FLAG(FILEOP_SILENT) ) {
- // Display "Disk is read-only" warning
- CResString<200> sText(IDS_VOLUMEREADONLY);
- CResString<64> sCaption(IDS_WARNING);
- ::MessageBox(NULL, sText, sCaption, MB_OK | MB_ICONEXCLAMATION | MB_SETFOREGROUND);
- }
- // Make sure to abort operation completely after this
- if( pdwFileOpFlags!=NULL ) *pdwFileOpFlags |= FILEOP_CANCEL;
- return FALSE;
- }
- return TRUE;
- }
- // Prompts user for confirmation of file delete
- // Returns:
- // IDYES - if the user acceps
- // IDNO - if the user denies
- UINT CFolder::_ConfirmDelete(LPCITEMIDLIST *pidls, UINT nCount, LPDWORD pdwFileOpFlags)
- {
- ATLASSERT(pidls);
- ATLASSERT(nCount>0);
- if( FILEOP_FLAG(FILEOP_SILENT) ) return IDYES;
- if( FILEOP_FLAG(FILEOP_YESTOALL) ) return IDYES;
- TCHAR szText[MAXNAMELEN+64];
- if( nCount==1 ) {
- CResString<64> sFormat(IDS_DELETEFILE);
- TCHAR szName[MAXNAMELEN+1];
- PidlGetName(szName, pidls[0]);
- ::wsprintf(szText, sFormat, szName);
- }
- else {
- CResString<64> sFormat(IDS_DELETEFILES);
- ::wsprintf(szText, sFormat, nCount);
- }
- CResString<64> sCaption(IDS_CONFIRMATION);
- return ::MessageBox(::GetActiveWindow(), szText, sCaption, MB_ICONQUESTION | MB_YESNO);
- }
- // Checks for file collision in the current directory.
- // Will bring up Duplicate dialog to allow user response.
- // The 'pdwFileOpFlags' and 'pbIsDuplicate' arguments are optional,
- // and will be updated accordingly if supplied.
- // Returns:
- // IDOK - if success
- // IDNO or IDCANCEL - if cancelled or fails.
- UINT CFolder::_CheckFileCollision(CAdfVolume &vol, LPCTSTR pstrFileName, DWORD *pdwFileOpFlags, BOOL *pbIsDuplicate)
- {
- ATLASSERT(pstrFileName);
- ATLASSERT(vol.IsOpen());
- // Do an initial check for flags and settings
- if( pbIsDuplicate!=NULL ) *pbIsDuplicate = FALSE;
- if( FILEOP_FLAG(FILEOP_CANCEL) ) return IDCANCEL;
- USES_CONVERSION;
- CResString<32> sType;
- // Scan the directory list for an existing entry...
- CAdfDirList list;
- if( vol.GetCurrentDirctory(list)==FALSE ) return IDCANCEL;
- List *cell = list;
- bool bFound = false;
- while( cell!=NULL ) {
- Entry *entry = (Entry *)cell->content;
- if( _tcsicmp(A2CT(entry->name), pstrFileName)==0 ) {
- sType.LoadString( CAdfFile::_IsDirectory(entry->type) ? IDS_FOLDER : IDS_FILE );
- bFound = true;
- break;
- }
- cell = cell->next;
- }
- if( !bFound ) return IDOK; // No collision detected, just continue...
- // Update flags
- if( pbIsDuplicate!=NULL ) *pbIsDuplicate = TRUE;
- if( FILEOP_FLAG(FILEOP_YESTOALL) ) return IDOK;
- if( FILEOP_FLAG(FILEOP_SILENT) ) return IDOK;
- // Show Duplicate dialog
- CDuplicateDlg dlg;
- dlg.m_pstrFileName = pstrFileName;
- dlg.m_pstrType = sType;
- UINT res = dlg.DoModal();
- switch( res ) {
- case IDCANCEL:
- if( pdwFileOpFlags!=NULL ) *pdwFileOpFlags |= FILEOP_CANCEL;
- return IDCANCEL;
- case IDNO:
- return IDNO;
- case IDOK:
- case IDYES:
- return IDOK;
- case IDYESTOALL:
- if( pdwFileOpFlags!=NULL ) *pdwFileOpFlags |= FILEOP_YESTOALL;
- return IDOK;
- }
- return IDOK;
- }
- // Scan the directory list repeatingly until we
- // find a unique name entry.
- // If a name collision is detected, a number is appended and incremented
- // until a unique name is found.
- BOOL CFolder::_FindUniqueFileName(CAdfVolume &vol, LPTSTR pstrName)
- {
- ATLASSERT(!::IsBadWritePtr(pstrName, MAXNAMELEN));
- ATLASSERT(vol.IsOpen());
- USES_CONVERSION;
- const int MAX_TRIES = 200;
- CAdfDirList list;
- if( vol.GetCurrentDirctory(list)==FALSE ) return FALSE;
- TCHAR szNewName[MAXNAMELEN+1];
- bool bFoundUnique = false;
- for( int i=1; !bFoundUnique && (i<MAX_TRIES); i++ ) {
- if( i>1 ) ::wsprintf(szNewName, _T("%s (%d)"), pstrName, i); else _tcscpy( szNewName, pstrName );
- List *cell = list;
- bool bFoundName = false;
- while( cell!=NULL ) {
- Entry *entry = (Entry *)cell->content;
- if( _tcsicmp(A2CT(entry->name), szNewName)==0 ) {
- bFoundName = true;
- break;
- }
- cell = cell->next;
- }
- bFoundUnique = !bFoundName;
- }
- if( !bFoundUnique ) return FALSE;
- _tcscpy( pstrName, szNewName );
- return TRUE;
- }
- // Make sure the filename is valid on the Amiga.
- // The 'pstrName' string argument may be changed/truncated.
- // I don't remember all the limitations of the file system,
- // but at least we check length and sanity check for the most
- // obvious illegal characters.
- BOOL CFolder::_ValidateFileName(LPTSTR pstrName, LPDWORD pdwFileOpFlags)
- {
- ATLASSERT(!::IsBadWritePtr(pstrName,MAXNAMELEN));
- // If user don't wanna truncate, then return error if name is too long
- if( FILEOP_FLAG(FILEOP_DONTTRUNCATE) ) {
- if( _tcslen(pstrName)>MAXNAMELEN ) return FALSE;
- }
- // Check for known illegal characters
- if( _tcspbrk(pstrName, _T("<>/#?:"))!=NULL ) return FALSE;
- // Truncate string
- pstrName[MAXNAMELEN] = _T(' ');
- return TRUE;
- }