2025-11-27 16:46:48 +09:00

1162 lines
30 KiB
C++

// ==========================================================================
// Class Implementation : COXDirSpec
// ==========================================================================
// Source file : dir.cpp
// Version: 9.3
// This software along with its related components, documentation and files ("The Libraries")
// is © 1994-2007 The Code Project (1612916 Ontario Limited) and use of The Libraries is
// governed by a software license agreement ("Agreement"). Copies of the Agreement are
// available at The Code Project (www.codeproject.com), as part of the package you downloaded
// to obtain this file, or directly from our office. For a copy of the license governing
// this software, you may contact us at legalaffairs@codeproject.com, or by calling 416-849-8900.
// //////////////////////////////////////////////////////////////////////////
#include "stdafx.h" // standard MFC include
#include "dir.h" // class specification
#include "path.h" // For MakeUnique() and FindLowerDirectory()
#include "returns.h" // internal return codes
#ifndef NOT_REGISTERED
#include "copytree.h" // For copying of entire dir trees
#include "cpystdlg.h" // For displaying info while copying dir trees
#endif // !NOT_REGISTERED
#include <direct.h> // For directory functions (getdcwd(), ...)
#include <stdlib.h> // For constant definitions (_MAX_DIR, ...)
#include <dos.h> // For _find_t, _dos_findfirst, ...
#include "UTB64Bit.h"
#define _A_ALL _A_ARCH | _A_HIDDEN | _A_NORMAL | _A_RDONLY | _A_SYSTEM
#ifdef _DEBUG
#undef THIS_FILE
static char BASED_CODE THIS_FILE[] = __FILE__;
#endif
#include "filelmt.h" // for invalid chars
IMPLEMENT_SERIAL(COXDirSpec, CObject, 0)
#define new DEBUG_NEW
/////////////////////////////////////////////////////////////////////////////
// Definition of static members
// Data members -------------------------------------------------------------
// protected:
// private:
// CString m_sDrive;
// --- The drive specification consists of a drive letter (capital letter)
// followed be a colon
//CString m_sSubdirectory;
// --- A subdirecory consists of the directory specification, without the drive
// and file specification
// In case of the root directory, it ends in a back slash,
// otherwise it never ends in a back slash
// Member functions ---------------------------------------------------------
// public:
COXDirSpec::COXDirSpec()
:
m_sDrive(_T("")),
m_sSubdirectory(_T(""))
{
}
COXDirSpec::COXDirSpec(LPCTSTR pszDirectory)
{
// ... Must be valid pointer
ASSERT(pszDirectory != NULL);
if (!SetDirectory(pszDirectory))
{
TRACE(_T("COXDirSpec::COXDirSpec : An invalid directory (%s) was specified, clearing object\n"),
pszDirectory);
Empty();
}
}
COXDirSpec::COXDirSpec(const COXDirSpec& dirSrc)
:
m_sDrive(dirSrc.m_sDrive),
m_sSubdirectory(dirSrc.m_sSubdirectory)
{
}
BOOL COXDirSpec::AppendDirectory(const COXDirSpec& dirSecond)
{
// The drive spec of dirSecond must be empty or the same as
// that of *this
if (!dirSecond.GetDrive().IsEmpty() && (GetDrive() != dirSecond.GetDrive()))
{
TRACE(_T("COXDirSpec::AppendDirectory : Illegal drive spec (%s != %s)\n"),
(LPCTSTR)GetDrive(), (LPCTSTR)dirSecond.GetDrive());
return FALSE;
}
if (dirSecond.GetSubdirectory().IsEmpty())
return TRUE;
if (dirSecond.GetSubdirectory().Left(1) == _T("\\"))
{
TRACE(_T("COXDirSpec::AppendDirectory : Second dir cannot start with back slash (%s)\n"),
dirSecond.GetSubdirectory());
return FALSE;
}
CString sSubdir = GetSubdirectory();
if (!sSubdir.IsEmpty() && (sSubdir.Right(1) != _T("\\")))
sSubdir += _T("\\");
return SetSubdirectory(sSubdir += dirSecond.GetSubdirectory());
}
COXDirSpec& COXDirSpec::operator=(const COXDirSpec& dirSrc)
{
if(this==&dirSrc)
return *this;
m_sDrive = dirSrc.m_sDrive;
m_sSubdirectory = dirSrc.m_sSubdirectory;
return *this;
}
CString COXDirSpec::GetDrive() const
{
return m_sDrive;
}
BOOL COXDirSpec::SetDrive(LPCTSTR pszDrive)
{
// ... Must be valid pointer
ASSERT(pszDrive != NULL);
if (_tcsclen(pszDrive) == 0)
{
m_sDrive = pszDrive;
return TRUE;
}
ASSERT(_tcsclen(pszDrive) != 0);
if ( ((_T('a') <= pszDrive[0]) && (pszDrive[0] <= _T('z'))) ||
((_T('A') <= pszDrive[0]) && (pszDrive[0] <= _T('Z')))
)
{
m_sDrive = CString(TCHAR(toupper(pszDrive[0]))) + _T(':');
return TRUE;
}
else
{
TRACE(_T("COXDirSpec::SetDrive : Invalid drive specification (%c)\n"), pszDrive[0]);
return FALSE;
}
}
void COXDirSpec::ForceSetDrive(LPCTSTR pszDrive)
{
// ... Must be valid pointer
ASSERT(pszDrive != NULL);
if ((0 < _tcsclen(pszDrive)) &&
( ((_T('a') <= pszDrive[0]) && (pszDrive[0] <= _T('z'))) ||
((_T('A') <= pszDrive[0]) && (pszDrive[0] <= _T('Z')))
)
)
VERIFY(SetDrive(CString(TCHAR(toupper(pszDrive[0]))) + _T(":")));
else
VERIFY(SetDrive(_T("")));
}
CString COXDirSpec::GetSubdirectory() const
{
return m_sSubdirectory;
}
BOOL COXDirSpec::SetSubdirectory(LPCTSTR pszSubdirectory)
{
// ... Must be valid pointer
ASSERT(pszSubdirectory != NULL);
CString sSubdirectory(pszSubdirectory);
// ... Short circuit evaluation
if ( (sSubdirectory.Right(1) != _T("\\")) || (sSubdirectory.GetLength() == 1))
{
#ifndef OX_FILEMNG_NOCHANGECASE
sSubdirectory.MakeUpper();
#endif
if ((int)_tcscspn(sSubdirectory, INVALID_DIR_CHARS) != sSubdirectory.GetLength())
{
TRACE(_T("COXFileSpec::SetSubdirectory : Subdirectorye (%s) contains illegal characters\n"), sSubdirectory);
return FALSE;
}
m_sSubdirectory = sSubdirectory;
return TRUE;
}
else
{
TRACE(_T("COXDirSpec::SetSubdirectory : Only root directory may end in \"\\\", not %s\n"), sSubdirectory);
return FALSE;
}
}
void COXDirSpec::ForceSetSubdirectory(LPCTSTR pszSubdirectory)
{
// ... Must be valid pointer
ASSERT(pszSubdirectory != NULL);
CString sSubdirectory(pszSubdirectory);
int nIndexWrongChar;
while ((nIndexWrongChar = (int)_tcscspn(sSubdirectory, INVALID_DIR_CHARS)) != sSubdirectory.GetLength())
{
sSubdirectory = sSubdirectory.Left(nIndexWrongChar) + sSubdirectory.Mid(nIndexWrongChar + 1);
}
if ( (sSubdirectory.Right(1) == _T("\\")) && (sSubdirectory.GetLength() != 1))
// Remove trailing back slash
sSubdirectory = sSubdirectory.Mid(0,sSubdirectory.GetLength() - 1);
VERIFY(SetSubdirectory(sSubdirectory));
}
COXDirSpec COXDirSpec::GetLastSubdirectory() const
{
COXDirSpec resultDir;
CString sSubdir;
int nIndex;
resultDir.SetDrive(GetDrive());
sSubdir = GetSubdirectory();
nIndex = sSubdir.ReverseFind(_T('\\'));
if (nIndex == -1)
// No back slash found
resultDir.SetSubdirectory(GetSubdirectory());
else
{
ASSERT(0 <= nIndex);
resultDir.SetSubdirectory(sSubdir.Mid(nIndex + 1));
}
return resultDir;
}
void COXDirSpec::RemoveLastSubdirectory()
{
int nIndex;
nIndex = m_sSubdirectory.ReverseFind(_T('\\'));
if (nIndex != -1)
// Back slash found
{
ASSERT(0 <= nIndex);
if (nIndex != 0)
// Not the root directory
m_sSubdirectory = m_sSubdirectory.Left(nIndex);
else
// The root directory
m_sSubdirectory = _T("\\");
}
}
CString COXDirSpec::GetDirectory() const
{
return m_sDrive + m_sSubdirectory;
}
BOOL COXDirSpec::SetDirectory(LPCTSTR pszDirectory)
{
// ... Must be valid pointer
ASSERT(pszDirectory != NULL);
CString sDirectory(pszDirectory);
if (! sDirectory.IsEmpty() && sDirectory.Mid(1,1) == _T(":"))
{
if (!SetDrive(sDirectory.Left(2)))
return FALSE;
if (3 <= sDirectory.GetLength())
return SetSubdirectory(sDirectory.Mid(2));
else
return SetSubdirectory(_T(""));
}
else
{
return SetDrive(_T("")) && SetSubdirectory(sDirectory);
}
}
void COXDirSpec::ForceSetDirectory(LPCTSTR pszDirectory)
{
// ... Must be valid pointer
ASSERT(pszDirectory != NULL);
if ((0 < _tcsclen(pszDirectory)) && (pszDirectory[1] == _T(':')))
{
ForceSetDrive(&pszDirectory[0]);
if (3 <= _tcsclen(pszDirectory))
ForceSetSubdirectory(&pszDirectory[2]);
else
ForceSetSubdirectory(_T(""));
}
else
{
ForceSetDrive(_T(""));
ForceSetSubdirectory(pszDirectory);
}
}
CString COXDirSpec::GetFileSystemType()
{
if (m_sDrive.IsEmpty())
{
TRACE(_T("COXDirSpec::GetFileSystemType : No drive specified, returning empty string\n"));
return _T("");
}
#ifdef WIN32
const int nMaxSystemNameLength = 50;
TCHAR szSystemName[nMaxSystemNameLength + 1];
BOOL bSuccess;
// ... The Error Mode is temporarily set to SEM_FAILCRITICALERRORS to
// allow failures to immediately return to the calling program.
// This eliminates unwanted dialog boxes that prompt for disks
// to be placed in the drive.
UINT nOldErrorMode = ::SetErrorMode(SEM_FAILCRITICALERRORS);
bSuccess = GetVolumeInformation(m_sDrive + _T("\\"), NULL, 0, NULL,
NULL, NULL, szSystemName, nMaxSystemNameLength);
// ... Restore the error code
::SetErrorMode(nOldErrorMode);
if (bSuccess)
return szSystemName;
else
{
TRACE(_T("COXDirSpec::GetFileSystemType : Could not determine file system, returning empty string\n"));
return _T("");
}
#else
// WIN16 cannot distinguish between file systems, so always return "FAT"
return _T("FAT");
#endif
}
BOOL COXDirSpec::MakeTemp()
{
#ifdef WIN32
CString sTempPath;
BOOL bSuccess = ::GetTempPath(_MAX_PATH, sTempPath.GetBuffer(_MAX_PATH));
sTempPath.ReleaseBuffer();
if (bSuccess)
{
// we'll have to use FORCE... because the path returned
// by WIN32 api ends with a '\'
ForceSetDirectory(sTempPath);
return TRUE;
}
else
return FALSE;
#else
char path_buffer[_MAX_PATH + 1];
char chDriveLetter = 'C';
char* pszSlash = NULL;
// Get temp path
::GetTempFileName(0, // Use the default drive
_T("TMP"), // TMP-prefix
1, // Do not try to open and close the file
path_buffer); // Result
// ... Remove the file name (keep only the dir, WITHOUT trailing black slash!)
pszSlash = strrchr(path_buffer, '\\');
if (pszSlash != NULL)
*pszSlash = '\0';
if (*path_buffer != '\0')
{
return SetDirectory(path_buffer);
}
else
{
TRACE(_T("COXDirSpec::MakeTemp : Could not make temp path, using root of temp drive\n"));
VERIFY(SetDirectory(_T("\\")));
return SetDrive(CString(::GetTempDrive(chDriveLetter)));
}
#endif
}
BOOL COXDirSpec::MakeUnique()
{
COXPathSpec path;
// First convert to file name and at the end convert back to dir
path.SetPath(GetDirectory());
if (path.MakeUnique() && SetDirectory(path.GetPath())) // Short circuit evaluation
return TRUE;
else
{
TRACE(_T("COXDirSpec::MakeUnique : Could not make unique dir\n"));
return FALSE;
}
}
BOOL COXDirSpec::MakeLargestExisting()
{
// Create a temp path spec to make the dir spec absolute
COXPathSpec absolutePath;
// .... Dir spec should already be avlid and file spec is valid too,
// so path spec should be valid as well !
VERIFY(absolutePath.SetPath(*this, COXFileSpec(_T("dummy.tmp"))));
if (!absolutePath.MakeAbsolute())
{
TRACE(_T("COXDirSpec::MakeLargestExisting : Could not make '%s' absolute, failing\n"),
(LPCTSTR)absolutePath.GetPath());
// ... Use root directory (best we can do)
VERIFY(SetDrive(absolutePath.GetDrive()));
VERIFY(SetSubdirectory(_T("\\")));
return FALSE;
}
// ... Use direcory part of absoluite path
operator=(absolutePath);
// First check whether the dir spec exists entirely
if (Exists())
return TRUE;
// Now iterate all the subdirectories of this absolute dir spec
// starting from the root, until all subdirs are tried or a non-existing
// subdirectory has been encountered
// E.g. if dir spec = C:\ONE\TWO\THREE then
// then try C:\
// then try C:\ONE
// then try C:\ONE\TWO
// If one of them does not exist, the search is stopped
LPCTSTR pszSubdirBegin;
LPCTSTR pszPrevSubdirEnd;
LPCTSTR pszSubdirEnd;
BOOL bExist = TRUE;
COXDirSpec testDir;
pszSubdirBegin = m_sSubdirectory;
pszPrevSubdirEnd = pszSubdirBegin;
pszSubdirEnd = _tcschr(pszSubdirBegin, _T('\\'));
while (bExist && (pszSubdirEnd != NULL))
{
// ... End the subdir spec BEFORE the terminating back slash
// except when the root (first char is back slash)
if (pszSubdirBegin == pszSubdirEnd)
pszSubdirEnd++;
VERIFY(testDir.SetDrive(GetDrive()));
VERIFY(testDir.SetSubdirectory(CString(pszSubdirBegin, PtrToInt(pszSubdirEnd - pszSubdirBegin))));
bExist = testDir.Exists();
if (bExist)
{
// Get next subdir
pszPrevSubdirEnd = pszSubdirEnd;
pszSubdirEnd = _tcschr(pszSubdirEnd + 1, _T('\\'));
}
}
if (bExist)
{
// All the checked subdirectories exist,
// and we know that the complete dir spec does not exist
ASSERT(!Exists());
ASSERT(pszSubdirEnd == NULL);
ASSERT(pszPrevSubdirEnd != NULL);
VERIFY(SetSubdirectory(CString(pszSubdirBegin, PtrToInt(pszPrevSubdirEnd - pszSubdirBegin))));
}
else
{
// A non-existing subdir has been encountered,
// use the last existing sub dir
ASSERT(pszPrevSubdirEnd != NULL);
VERIFY(SetSubdirectory(CString(pszSubdirBegin, PtrToInt(pszPrevSubdirEnd - pszSubdirBegin))));
}
// The dir spec must exist now
ASSERT(Exists());
return TRUE;
}
BOOL COXDirSpec::Exists() const
{
// Assume that an empty directory or the root directory always exist
// (although CFile::GetStatus("C:\\") return FALSE)
if (GetSubdirectory().IsEmpty() || (GetSubdirectory() == _T("\\")))
return TRUE;
CFileStatus fileStatus;
return ( (CFile::GetStatus(GetDirectory(), fileStatus)) &&
(fileStatus.m_attribute & CFile::directory) );
}
BOOL COXDirSpec::IsEmpty() const
{
return GetDirectory().IsEmpty();
}
void COXDirSpec::Empty()
{
m_sDrive.Empty();
m_sSubdirectory.Empty();
}
BOOL COXDirSpec::IsEmptyDir() const
{
COXPathSpec filePath;
BOOL bFileFound(FALSE);
BOOL bValid(FALSE);
VERIFY(filePath.SetPath(*this, COXFileSpec(_T("*.*"))));
#ifdef WIN32
WIN32_FIND_DATA fileData;
HANDLE hFindFile = FindFirstFile(filePath.GetPath(), &fileData);
if (hFindFile != INVALID_HANDLE_VALUE)
bFileFound = TRUE;
// As long a something is found, but is not a good one, keep searching
// ... Ignore non-subdirectories and subdirectories starting with a full steop
while (!bValid && bFileFound)
{
if (fileData.cFileName[0] != _T('.'))
bValid = TRUE;
if (!bValid)
bFileFound = FindNextFile(hFindFile, &fileData);
}
if (hFindFile != INVALID_HANDLE_VALUE)
FindClose(hFindFile);
#else
_find_t fileInfo;
bFileFound = !_dos_findfirst(filePath.GetPath(), _A_SUBDIR | _A_ALL, &fileInfo);
while (!bValid && bFileFound)
{
if ((strcmp(fileInfo.name, _T(".")) != 0) && (strcmp(fileInfo.name, _T("..")) != 0))
bValid = TRUE;
if (!bValid)
bFileFound = (_dos_findnext(&fileInfo) == 0);
}
#endif
return !bValid;
}
BOOL COXDirSpec::DoGetCurrentDir()
{
TCHAR pszSubdirectory[_MAX_DIR];
#ifndef WIN32
if(_getcwd(pszSubdirectory, _MAX_DIR) != NULL)
#else
if (GetCurrentDirectory(_MAX_DIR, pszSubdirectory))
#endif
{
ASSERT(CString(pszSubdirectory).GetLength() >= 2);
SetDrive(CString(pszSubdirectory).Left(2));
SetSubdirectory(CString(pszSubdirectory).Mid(2));
return TRUE;
}
else
{
TRACE(_T("COXDirSpec::DoGetCurrentDir : Error occurred while accessing current directory\n"));
return FALSE;
}
}
BOOL COXDirSpec::DoSetCurrentDir() const
{
if (!m_sDrive.IsEmpty())
{
ASSERT(m_sDrive[0] == toupper(m_sDrive[0]));
if (_chdrive((int)m_sDrive[0] - _T('A') + 1) != 0)
{
TRACE(_T("COXDirSpec::DoSetCurrentDir : Drive change to %s failed\n"), m_sDrive);
return FALSE;
}
}
if (!m_sSubdirectory.IsEmpty())
{
if (_tchdir((LPCTSTR)m_sSubdirectory) != 0)
{
TRACE(_T("COXDirSpec::DoSetCurrentDir : Directory change to %s failed\n"), m_sSubdirectory);
return FALSE;
}
}
return TRUE;
}
UINT COXDirSpec::DoGetDriveType()
{
if (m_sDrive.IsEmpty())
{
TRACE(_T("COXDirSpec::DoGetDriveType : No drive specified, returning empty string\n"));
return 0;
}
ASSERT(m_sDrive[0] == toupper(m_sDrive[0]));
#ifndef WIN32
int iDrive;
UINT uiType;
BOOL fCDROM=FALSE;
BOOL fRAM=FALSE;
iDrive = m_sDrive[0] - _T('A');
uiType = GetDriveType(iDrive);
// Check for CDROM on FIXED and REMOTE drives only
if (DRIVE_FIXED==uiType || DRIVE_REMOTE==uiType)
{
_asm
{
mov ax,1500h // Check if MSCDEX exists
xor bx,bx
int 2fh
or bx,bx // BX unchanged if MSCDEX is not around
jz CheckRAMDrive // No? Go check for RAM drive.
mov ax,150Bh // Check if drive is using CD driver
mov cx,iDrive
int 2fh
mov fCDROM,ax // AX if the CD-ROM flag
or ax,ax
jnz Exit // Leave if we found a CD-ROM drive.
CheckRAMDrive:
}
}
// Check for RAM drives on FIXED disks only.
if (DRIVE_FIXED==uiType)
{
/*
* Check for RAM drive is done by reading the boot sector and
* looking at the number of FATs. Ramdisks only have 1 while
* all others have 2.
*/
_asm
{
push ds
mov bx,ss
mov ds,bx
sub sp,0200h //Reserve 512 bytes to read a sector
mov bx,sp //and point BX there.
mov ax,iDrive //Read the boot sector of the drive
mov cx,1
xor dx,dx
int 25h
add sp,2 //Int 25h requires our stack cleanup.
jc DriveNotRAM
mov bx,sp
cmp ss:[bx+15h],0f8h //Reverify fixed disk
jne DriveNotRAM
cmp ss:[bx+10h],1 //Check if there's only one FATs
jne DriveNotRAM
mov fRAM,1
DriveNotRAM:
add sp,0200h
pop ds
Exit:
//Leave fRAM untouched it's FALSE by default.
}
}
/*
* If either CD-ROM or RAM drive flags are set, return privately
* defined flags for them (outside of Win32). Otherwise return
* the type given from GetDriveType.
*/
if (fCDROM)
return DRIVE_CDROM;
if (fRAM)
return DRIVE_RAMDISK;
//Drive B on a one drive system returns < 2 from GetDriveType.
return uiType;
#else
return GetDriveType(m_sDrive + _T("\\"));
#endif
}
CString COXDirSpec::DoGetVolumeInformation()
{
if (m_sDrive.IsEmpty())
{
TRACE(_T("COXDirSpec::DoGetVolumeInformation : No drive specified, returning empty string\n"));
return _T("");
}
ASSERT(m_sDrive[0] == toupper(m_sDrive[0]));
#ifndef WIN32
struct _find_t findStruct;
if (_dos_findfirst(m_sDrive + _T("\\"), _A_VOLID, &findStruct) == 0)
{
// a volume label in 16bit can contain 11 chars. But it's written as
// 8.3 string on disk. So when asking this volume label a period appears in
// the label. We will remove it here. char* pszPeriod = NULL;
char* pszPeriod = strchr(findStruct.name, '.');
if (pszPeriod != NULL)
strcpy(pszPeriod, pszPeriod + 1);
return CString(findStruct.name);
}
return _T("");
#else
TCHAR VolumeName[_MAX_DIR + 1];
if (!GetVolumeInformation(m_sDrive + _T("\\"), VolumeName,_MAX_DIR, NULL,NULL,NULL,NULL,0))
{
TRACE(_T("COXDirSpec::DoGetVolumeInformation : GetVolumeInformation failed\n"));
return _T("");
}
return CString(VolumeName);
#endif
}
DWORD COXDirSpec::DoGetDiskFreeSpace()
{
DWORD dwBytes;
if (m_sDrive.IsEmpty())
{
TRACE(_T("COXDirSpec::DoGetDiskFreeSpace : No drive specified, returning zero space\n"));
return 0;
}
ASSERT(m_sDrive[0] == toupper(m_sDrive[0]));
#ifndef WIN32
struct _diskfree_t DriveInfo;
int iDrive;
iDrive = m_sDrive[0] - 'A' + 1;
if (_dos_getdiskfree(iDrive, &DriveInfo) == 0)
{
dwBytes = (long)DriveInfo.bytes_per_sector * (long)DriveInfo.sectors_per_cluster;
dwBytes *= (long)DriveInfo.avail_clusters;
return dwBytes;
}
else
{
TRACE(_T("COXDirSpec::DoGetDiskFreeSpace : _dos_getdiskfree failed\n"));
return -1;
}
#else
BOOL bOk;
DWORD dwSectPerClust, dwBytesPerSect, dwFreeClust, dwTotClust;
bOk = GetDiskFreeSpace(m_sDrive + _T("\\"), &dwSectPerClust, &dwBytesPerSect, &dwFreeClust, &dwTotClust);
if (bOk)
{
dwBytes = dwFreeClust * dwSectPerClust * dwBytesPerSect;
return dwBytes;
}
else
{
TRACE(_T("COXDirSpec::DoGetDiskFreeSpace : GetDiskFreeSpace failed\n"));
return 0;
}
#endif
}
BOOL COXDirSpec::DoMakeNew() const
{
// Do not try to create a directory with an empty name and
// do not try to create the root directory (it already exists)
if (!m_sSubdirectory.IsEmpty() && (m_sSubdirectory != _T("\\")))
{
CString sDir = m_sDrive + m_sSubdirectory;
// First try to create all the directories in front of the last back slash
// This is done without error checking, because they may already exist
int nSlashPosition;
int nDeltaSlashPosition;
nDeltaSlashPosition = sDir.Find(_T('\\'));
nSlashPosition = nDeltaSlashPosition;
while ( nDeltaSlashPosition != -1)
{
#ifndef WIN32
_mkdir(sDir.Left(nSlashPosition));
#else
SECURITY_ATTRIBUTES sSecAttr;
sSecAttr.nLength = sizeof(sSecAttr);
sSecAttr.lpSecurityDescriptor = NULL;
sSecAttr.bInheritHandle = FALSE;
CreateDirectory(sDir.Left(nSlashPosition), &sSecAttr);
#endif
// ... Only root dir can end in \\ and this case is excluded here by spanning is
ASSERT(sDir.Right(1) != _T("\\"));
nDeltaSlashPosition = sDir.Mid(nSlashPosition + 1).Find(_T('\\'));
nSlashPosition += nDeltaSlashPosition + 1;
}
// Now try to create the entire directory specification,
// with error checking
#ifndef WIN32
if (_mkdir(sDir) != 0)
{
TRACE(_T("COXDirSpec::DoMakeNew : Directory creation %s failed\n"), sDir);
return FALSE;
}
#else
SECURITY_ATTRIBUTES sSecAttr2;
sSecAttr2.nLength = sizeof(sSecAttr2);
sSecAttr2.lpSecurityDescriptor = NULL;
sSecAttr2.bInheritHandle = FALSE;
if (!CreateDirectory(sDir, &sSecAttr2))
{
TRACE(_T("COXDirSpec::DoMakeNew : Directory creation %s failed\n"), sDir);
return FALSE;
}
#endif
}
return TRUE;
}
#ifndef NOT_REGISTERED
BOOL COXDirSpec::DoCopyDirectory(COXDirSpec DestDirectory, BOOL bOnlyContents /* = TRUE */,
BOOL bCleanUp /* = FALSE */, COXCopyStatusDialog* pCpyStatDlg /* = NULL */)
{
COXCopyTree CopyTree;
return CopyTree.DoCopyTree(*this, DestDirectory, bOnlyContents, bCleanUp, pCpyStatDlg);
}
#endif // !NOT_REGISTERED
BOOL COXDirSpec::DoRemove(BOOL bRecursively /* = FALSE */,
BOOL bAlsoRemoveReadOnly /* = FALSE */) const
{
#ifdef _DEBUG
if (GetSubdirectory() == _T("\\"))
{
TRACE(_T("COXDirSpec::DoRemove : Trying to remove root directory (%s) and all it's subdirectories\n"),
GetSubdirectory());
// Dangerous situation : Give user change to abort or go on (retry)
ASSERT(FALSE);
}
#endif
if (!m_sSubdirectory.IsEmpty())
{
if (bRecursively)
// First remove all underlying directories
{
COXDirSpec lowerDir;
lowerDir = FindLowerDirectory();
while (!lowerDir.IsEmpty())
{
if (!lowerDir.DoRemove(bRecursively, bAlsoRemoveReadOnly))
// Could not remove lower directory, abort
return FALSE;
lowerDir = FindLowerDirectory();
}
}
// Remove all the files in the directory
if (!RemoveAllFiles(bAlsoRemoveReadOnly))
{
TRACE(_T("COXDirSpec::DoRemove : Removal of files failed, cannot remove directory %s\n"), GetDirectory());
return FALSE;
}
// Remove the directory itself
#ifdef WIN32
if (!RemoveDirectory(GetDirectory()))
#else
if (_rmdir(GetDirectory()) != 0)
#endif
{
TRACE(_T("COXDirSpec::DoRemove : Cannot remove directory : %s\n"), GetDirectory());
return FALSE;
}
}
return TRUE;
}
void COXDirSpec::Serialize(CArchive& ar)
{
CObject::Serialize(ar);
TRY
{
if (ar.IsStoring())
{
ar << m_sDrive;
ar << m_sSubdirectory;
}
else
{
ar >> m_sDrive;
ar >> m_sSubdirectory;
}
}
CATCH(CException, e)
{
if (ar.IsStoring())
TRACE0("An Exception was thrown while storing a COXDirSpec");
else
TRACE0("An Exception was thrown while loading a COXDirSpec");
THROW_LAST();
}
END_CATCH
}
BOOL COXDirSpec::operator==(const COXDirSpec& dirSpec) const
{
return ( (m_sDrive == dirSpec.m_sDrive) &&
(m_sSubdirectory == dirSpec.m_sSubdirectory) );
}
BOOL COXDirSpec::operator!=(const COXDirSpec& dirSpec) const
{
return ( (m_sDrive != dirSpec.m_sDrive) ||
(m_sSubdirectory != dirSpec.m_sSubdirectory) );
}
BOOL COXDirSpec::operator<=(const COXDirSpec& dirSpec) const
{
return (m_sDrive + m_sSubdirectory <= dirSpec.m_sDrive + dirSpec.m_sSubdirectory);
}
BOOL COXDirSpec::operator<(const COXDirSpec& dirSpec) const
{
return (m_sDrive + m_sSubdirectory < dirSpec.m_sDrive + dirSpec.m_sSubdirectory);
}
BOOL COXDirSpec::operator>=(const COXDirSpec& dirSpec) const
{
return (m_sDrive + m_sSubdirectory >= dirSpec.m_sDrive + dirSpec.m_sSubdirectory);
}
BOOL COXDirSpec::operator>(const COXDirSpec& dirSpec) const
{
return (m_sDrive + m_sSubdirectory > dirSpec.m_sDrive + dirSpec.m_sSubdirectory);
}
#ifdef _DEBUG
void COXDirSpec::Dump(CDumpContext& dc) const
{
CObject::Dump(dc);
dc << _T("\nm_sDrive : ") << m_sDrive;
dc << _T("\nm_sSubdirectory : ") << m_sSubdirectory;
}
void COXDirSpec::AssertValid() const
{
CObject::AssertValid();
}
#endif
COXDirSpec::~COXDirSpec()
{
}
#ifdef WIN32
BOOL COXDirSpec::IsChildDir(LPWIN32_FIND_DATA lpFindFileData) const
// --- In :
// --- Out :
// --- Returns : if lpFindFileData contains a subdir...
// --- Effect :
{
return (
((lpFindFileData->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) == FILE_ATTRIBUTE_DIRECTORY) &&
(lpFindFileData->cFileName[0] != _T('.')));
}
#endif
// protected:
COXDirSpec COXDirSpec::FindLowerDirectory() const
// --- In :
// --- Out :
// --- Returns : A directory that is a subdirectory of this dir spec
// --- Effect :
{
COXDirSpec lowerDir;
COXPathSpec searchPath;
VERIFY(searchPath.SetPath(*this, COXFileSpec(_T("*.*"))));
BOOL bDirFound(FALSE);
BOOL bFileFound(FALSE);
#ifdef WIN32
WIN32_FIND_DATA fileData;
HANDLE hFindFile = FindFirstFile(searchPath.GetPath(), &fileData);
if (hFindFile != INVALID_HANDLE_VALUE)
bFileFound = TRUE;
// As long a something is found, but is not a good one, keep searching
// ... Ignore non-subdirectories and subdirectories starting with a full steop
while (!bDirFound && bFileFound)
{
bDirFound = IsChildDir(&fileData);
if (!bDirFound)
bFileFound = FindNextFile(hFindFile, &fileData);
}
if (hFindFile != INVALID_HANDLE_VALUE)
FindClose(hFindFile);
if (bDirFound)
{
lowerDir = *this;
VERIFY(lowerDir.AppendDirectory(COXDirSpec(fileData.cFileName)));
return(lowerDir);
}
else
// Returning an empty directory
return(lowerDir);
#else
_find_t fileInfo;
bDirFound = !_dos_findfirst(searchPath.GetPath(), _A_SUBDIR | _A_ALL, &fileInfo);
// As long a something is found, but is not a good one, keep searching
// ... Ignore non-subdirectories and subdirectories starting with a full steop
while( (bDirFound) &&
( !((fileInfo.attrib & _A_SUBDIR) == _A_SUBDIR) || (*fileInfo.name == '.') ) )
bDirFound = !_dos_findnext(&fileInfo);
if (bDirFound)
{
lowerDir = *this;
VERIFY(lowerDir.AppendDirectory(COXDirSpec(fileInfo.name)));
return(lowerDir);
}
else
// Returning an empty directory
return(lowerDir);
#endif
}
BOOL COXDirSpec::RemoveAllFiles(BOOL bAlsoRemoveReadOnly /* = FALSE */) const
// --- In : bAlsoRemoveReadOnly : Whether to remove all the files,
// even when they are read only (TRUE) or not (FALSE)
// --- Out :
// --- Returns : Whether all the removals succeeded
// --- Effect : Removes all the files of this directory
{
COXPathSpec filePath;
VERIFY(filePath.SetPath(*this, COXFileSpec(_T("*.*"))));
BOOL bFileFound(TRUE);
#ifdef WIN32
WIN32_FIND_DATA fileData;
HANDLE hFindFile = FindFirstFile(filePath.GetPath(), &fileData);
// As long a something is found, but is not a good one, keep searching
// ... Ignore non-subdirectories and subdirectories starting with a full steop
if(hFindFile != INVALID_HANDLE_VALUE)
{
while(bFileFound)
{
if (IsChildDir(&fileData) || fileData.cFileName[0] == _T('.'))
{
bFileFound = FindNextFile(hFindFile, &fileData);
continue;
}
VERIFY(filePath.SetFileName(fileData.cFileName));
// If the file is Read/only and it is allowed to remove it, try so
if ((fileData.dwFileAttributes & FILE_ATTRIBUTE_READONLY) == FILE_ATTRIBUTE_READONLY)
{
if (bAlsoRemoveReadOnly)
{
if (!SetFileAttributes(filePath.GetPath(), fileData.dwFileAttributes & ~FILE_ATTRIBUTE_READONLY))
{
TRACE(_T("COXDirSpec::RemoveAllFiles : Could not change the Read/Only attribute of file %s\n"),
filePath.GetPath());
::FindClose(hFindFile);
return FALSE;
}
}
else
{
TRACE(_T("COXDirSpec::RemoveAllFiles : Could not remove the file %s, because it is Read/Only\n"),
filePath.GetPath());
::FindClose(hFindFile);
return FALSE;
}
}
if (!DeleteFile(filePath.GetPath()))
{
TRACE(_T("COXDirSpec::RemoveAllFiles : Could not remove the file %s\n"), filePath.GetPath());
::FindClose(hFindFile);
return FALSE;
}
bFileFound = FindNextFile(hFindFile, &fileData);
} //while
::FindClose(hFindFile);
} // if(INVALID_HANDLE_VALUE)
#else
_find_t fileInfo;
VERIFY(filePath.SetPath(*this, COXFileSpec(_T("*.*"))));
bFileFound = !_dos_findfirst(filePath.GetPath(), _A_ALL, &fileInfo);
while(bFileFound)
{
VERIFY(filePath.SetFileName(fileInfo.name));
// If the file is Read/only and it is allowed to remove it, try so
if ((fileInfo.attrib & _A_RDONLY) == _A_RDONLY)
{
if (bAlsoRemoveReadOnly)
{
if (_dos_setfileattr(filePath.GetPath(), fileInfo.attrib & ~_A_RDONLY) != 0)
{
TRACE(_T("COXDirSpec::RemoveAllFiles : Could not change the Read/Only attribute of file %s\n"),
filePath.GetPath());
return FALSE;
}
}
else
{
TRACE(_T("COXDirSpec::RemoveAllFiles : Could not remove the file %s, because it is Read/Only\n"),
filePath.GetPath());
return FALSE;
}
}
if (remove(filePath.GetPath()) != 0)
{
TRACE(_T("COXDirSpec::RemoveAllFiles : Could not remove the file %s\n"), filePath.GetPath());
return FALSE;
}
bFileFound = !_dos_findnext(&fileInfo);
}
#endif
return TRUE;
}
// private:
// Message handlers ---------------------------------------------------------
// ==========================================================================