// Path.cpp: implementation of the CPath class.
//
//////////////////////////////////////////////////////////////////////
#include "stdafx.h"
#include <atlbase.h>
#include <Shlobj.h>

//////////////////////////////////////////////////////////////////////
// Constants
//////////////////////////////////////////////////////////////////////

LPCTSTR const DLL_EXTENSION = _T("dll");
LPCTSTR const INI_EXTENSION = _T("ini");
LPCTSTR const EXE_EXTENSION = _T("exe");
LPCTSTR const WILD_NAME_EXTENSION = _T("*.*");
const TCHAR WILD_ONE             = '?';
const TCHAR WILD_ANY             = '*';
LPCTSTR const WILD_SET           = _T("?*");
LPCTSTR const DIR_DOUBLEDELIM    = _T("\\\\");
const TCHAR DRIVE_DELIMITER      = ':';
const TCHAR DIRECTORY_DELIMITER  = '\\';
const TCHAR EXTENSION_DELIMITER  = '.';
const TCHAR DIRECTORY_DELIMITER2 = '/';
HINSTANCE CPath::m_hInst = NULL;

//////////////////////////////////////////////////////////////////////
// Helpers
//////////////////////////////////////////////////////////////////////

void CPath::SethInst ( HINSTANCE hInst )
{
	m_hInst = hInst;
}

HINSTANCE CPath::GethInst()
{
	return m_hInst;
}

//-------------------------------------------------------------
// Task    : Create a string of length nDigits containing random digits
//-------------------------------------------------------------
static stdstr RandomDigits(int nDigits)
{
    // Keep the number of digits in a rational limit
    //ASSERT(nDigits >  0);
    //ASSERT(nDigits < 20);
    
	int    nDigits2 = nDigits;
	stdstr Digits;
    TCHAR  next_8_digits[9];
    while(nDigits2 > 0)
    {
        _stprintf(next_8_digits,_T("%08lx"),GetTickCount());

        for(int i=0; i<8; i++)
        {
            Digits += next_8_digits[i];
            if(--nDigits2 == 0)
                break;
        }
    }

    int last_digit =rand();
    if(last_digit < 0)
        last_digit =(-last_digit);
    last_digit %= 10;
    Digits[nDigits - 1] = (char)('0' + last_digit);
    
	return Digits;
}

//////////////////////////////////////////////////////////////////////
// Initialisation
//////////////////////////////////////////////////////////////////////

//-------------------------------------------------------------
// Task    : Helper function for the various CPath constructors. 
//           Initializes the data members and establishes various
//           class invariants
//-------------------------------------------------------------
inline void CPath::Init()
{
	m_dwFindFileAttributes =0;
	m_hFindFile =NULL;
}

//-------------------------------------------------------------
// Task    : Helper function for the various CPath destructors.
//           Cleans up varios internals
//-------------------------------------------------------------
inline void CPath::Exit()
{
	if(m_hFindFile != NULL)
    {
		FindClose(m_hFindFile);
        m_hFindFile =NULL;
    }
}

//////////////////////////////////////////////////////////////////////
// Construction/Destruction
//////////////////////////////////////////////////////////////////////

//-------------------------------------------------------------
// Task    : Constructs a path
//-------------------------------------------------------------
CPath::CPath()
{
	Init();
    Empty();
}

//-------------------------------------------------------------
// Task    : Constructs a path as copy of another
//-------------------------------------------------------------
CPath::CPath(const CPath& rPath)
{
	Init();
	m_strPath =rPath.m_strPath;
}

//-------------------------------------------------------------
// Task    : Constructs a path and points it 2 lpszPath
//-------------------------------------------------------------
CPath::CPath(LPCTSTR lpszPath)
{
	Init();
    m_strPath =lpszPath ? lpszPath : _T("");
	cleanPathString(m_strPath);
}

CPath::CPath(LPCTSTR lpszPath, LPCTSTR NameExten)
{
	Init();
	SetDriveDirectory(lpszPath);
	SetNameExtension(NameExten);
}

//-------------------------------------------------------------
// Task    : Constructs a path and points it 2 strPath
//-------------------------------------------------------------
CPath::CPath(const stdstr& strPath)
{
    Init();
    m_strPath =strPath;
	cleanPathString(m_strPath);
}

//-------------------------------------------------------------
// Task    : Constructs a path and points it 2 strPath
//-------------------------------------------------------------
CPath::CPath(const stdstr& strPath, LPCTSTR NameExten )
{
	Init();
	SetDriveDirectory(strPath.c_str());
	SetNameExtension(NameExten);
}

//-------------------------------------------------------------
// Task    : Constructs a path and points it 2 strPath
//-------------------------------------------------------------
CPath::CPath(const stdstr& strPath, const stdstr& NameExten )
{
	Init();
	SetDriveDirectory(strPath.c_str());
	SetNameExtension(NameExten.c_str());
}

//-------------------------------------------------------------
// Task    : Constructs a path and points it 2 specified 
//           special directory
//-------------------------------------------------------------
CPath::CPath(SpecialDirectoryType eInitialDir)
{
	Init();
	SpecialDirectory(eInitialDir);
}

CPath::CPath(SpecialDirectoryType eInitialDir, const stdstr & NameExten )
{
	Init();
	SpecialDirectory(eInitialDir);
	SetNameExtension(NameExten.c_str());
}

CPath::CPath(SpecialDirectoryType eInitialDir, LPCTSTR NameExten )
{
	Init();
	SpecialDirectory(eInitialDir);
	SetNameExtension(NameExten);
}
//-------------------------------------------------------------
// Task    : Cleanup and destruct a path object
//-------------------------------------------------------------
CPath::~CPath()
{
	Exit();
}


//-------------------------------------------------------------
// Post    : Return TRUE if paths are equal
// Task    : Check if the two path are the same
//-------------------------------------------------------------
BOOL CPath::operator ==(const CPath& rPath) const
{
    // Get fully qualified versions of the paths
	stdstr FullyQualified1;
    stdstr FullyQualified2;
	
	GetFullyQualified(FullyQualified1);
	rPath.GetFullyQualified(FullyQualified2);
	
    // Compare them
	return _tcsicmp(FullyQualified1.c_str(),FullyQualified2.c_str()) == 0;
}

//-------------------------------------------------------------
// Post    : Return TRUE if paths are different
// Task    : Check if the two path are different
//-------------------------------------------------------------
BOOL CPath::operator !=(const CPath& rPath) const
{
    return !(*this == rPath);
}

//-------------------------------------------------------------
// Task    : Assign a path 2 another
//-------------------------------------------------------------
CPath& CPath::operator =(const CPath& rPath)
{                   
	if(this != &rPath)
		m_strPath =rPath.m_strPath;
    return *this;
}

//-------------------------------------------------------------
// Post    : Return the path, so that assignements can be chained
// Task    : Assign a string 2 a path
//-------------------------------------------------------------
CPath& CPath::operator =(LPCTSTR lpszPath)
{
    m_strPath =lpszPath ? lpszPath : _T("");
    return *this;
}

//-------------------------------------------------------------
// Post    : Return the path, so that assignements can be chained
// Task    : Assign a string 2 a path
//-------------------------------------------------------------
CPath& CPath::operator =(const stdstr& strPath)
{
    m_strPath =strPath;
    return *this;
}

//-------------------------------------------------------------
// Post    : Converts path 2 string
// Task    : Convert path 2 string
//           Warning: because this pointer 2 string point in the data
//           of this class, it is possible 2 cast the result of this 
//           function in any non-constant pointer and alter the data.
//           Very dangerous
//-------------------------------------------------------------
CPath::operator LPCTSTR() const
{
    return (LPCTSTR)m_strPath.c_str();
}

CPath::CPath(DIR_CURRENT_DIRECTORY /*sdt*/, LPCTSTR NameExten)
{
	// Application's current directory	 
	Init();
	CurrentDirectory();
	if (NameExten) { SetNameExtension(NameExten); }
}

CPath::CPath(DIR_WINDOWS_DIRECTORY /*sdt*/, LPCTSTR NameExten)
{
	// Windows directory
	Init();
	WindowsDirectory();
	if (NameExten) { SetNameExtension(NameExten); }
}

CPath::CPath(DIR_SYSTEM_DIRECTORY /*sdt*/, LPCTSTR NameExten)
{
	// Windows' system directory    
	Init();
	SystemDirectory();
	if (NameExten) { SetNameExtension(NameExten); }
}

CPath::CPath(DIR_SYSTEM32_DIRECTORY /*sdt*/, LPCTSTR NameExten)
{
	Init();
	SystemDirectory();
	if (NameExten) { SetNameExtension(NameExten); }
}

CPath::CPath(DIR_SYSTEM_DRIVER_DIRECTORY /*sdt*/, LPCTSTR NameExten)
{
	Init();
	SystemDirectory();
	AppendDirectory(_T("drivers"));
	if (NameExten) { SetNameExtension(NameExten); }
}

CPath::CPath(DIR_SYSTEM_DRIVE_ROOT_DIRECTORY /*sdt*/, LPCTSTR NameExten)
{
	// The root directory of system drive
	Init();
	SystemDriveRootDirectory();
	if (NameExten) { SetNameExtension(NameExten); }
}

CPath::CPath(DIR_MODULE_DIRECTORY /*sdt*/, LPCTSTR NameExten)
{
	// The directory where the executable of this app is
	Init();
	ModuleDirectory();
	if (NameExten) { SetNameExtension(NameExten); }
}

CPath::CPath(DIR_MODULE_FILE /*sdt*/)
{
	// The directory where the executable of this app is
	Init();
	Module();
}

void CPath::SpecialDirectory(SpecialDirectoryType eInitialDir)
{
	switch(eInitialDir)
	{	
		// Windows temp directory
	case TEMP_DIRECTORY:
		TempDirectory();
		break;
		// Program files directory
	case PROGRAM_FILES_DIRECTORY:
		ProgramFilesDirectory();
		break;
		// Common files directory
	case COMMON_FILES_DIRECTORY:
		CommonFilesDirectory();
		break;
		// Accessories directory
	case ACCESSORIES_DIRECTORY:
		AccessoriesDirectory();
		break;
		// Media directory
	case MEDIA_DIRECTORY:
		MediaDirectory();
		break;
		// INF directory
	case DEVICE_DIRECTORY:
		DeviceDirectory();
		break;
		// User specific directories
	case USER_DESKTOP_DIRECTORY:
		UserDesktopDirectory();
		break;
	case USER_FAVORITES_DIRECTORY:
		UserFavoritesDirectory();
		break;
	case USER_FONTS_DIRECTORY:
		UserFontsDirectory();
		break;
	case USER_NETHOOD_DIRECTORY:
		UserNetworkNeighbourhoodDirectory();
		break;
	case USER_DOCUMENTS_DIRECTORY:
		UserDocumentsDirectory();
		break;
	case USER_RECENT_DIRECTORY:
		UserRecentDirectory();
		break;
	case USER_SENDTO_DIRECTORY:
		UserSendToDirectory();
		break;
	case USER_RECYCLE_DIRECTORY:
		UserRecycleBinDirectory();
		break;
	case USER_APPLICATION_DATA_DIRECTORY:
		UserApplicationDataDirectory();
		break;
	case USER_TEMPLATES_DIRECTORY:
		UserTemplatesDirectory();
		break;
	case USER_STARTMENU_DIRECTORY:
		UserStartMenuDirectory();
		break;
	case USER_STARTMENU_STARTUP_DIRECTORY:
		UserStartMenuStartupDirectory();
		break;
	case USER_STARTMENU_PROGRAMS_DIRECTORY:
		UserStartMenuProgramsDirectory();
		break;
		// Directories common 2 all users
	case COMMON_DESKTOP_DIRECTORY:
		CommonDesktopDirectory();
		break;
	case COMMON_STARTMENU_DIRECTORY:
		CommonStartMenuDirectory();
		break;
	case COMMON_STARTMENU_STARTUP_DIRECTORY:
		CommonStartMenuStartupDirectory();
		break;
	case COMMON_STARTMENU_PROGRAMS_DIRECTORY:
		CommonStartMenuProgramsDirectory();
		break;
		// Unknown special directory constant    
	default:
		// Accept only constants we know about
		//Bugger
		ATLASSERT(false);
		break;
	}
}

//-------------------------------------------------------------
// Post    : Returns the drive component without a colon, e.g. "c"
//           Returns the directory component with a leading backslash, 
//              but no trailing backslash, e.g. "\dir\subdir"
//           Returns name compleletely without delimiters, e.g "letter"
//           Returns extension completely without delimiters, e.g. "doc"
// Globals :
// I/O     :
// Task    : Return the individual components of this path. 
//           For any given argument, you can pass NULL if you are not 
//           interested in that component.
//           Do not rely on pNames being <= 8 characters, extensions 
//           being <= 3 characters, or drives being 1 character
//-------------------------------------------------------------
void CPath::GetComponents(stdstr* pDrive, 
                       	  stdstr* pDirectory, 
                       	  stdstr* pName, 
                          stdstr* pExtension) const
{
    TCHAR buff_drive[_MAX_DRIVE + 1];
    TCHAR buff_dir  [_MAX_DIR   + 1];
    TCHAR buff_name [_MAX_FNAME + 1];
    TCHAR buff_ext  [_MAX_EXT   + 1];

    ZeroMemory(buff_drive,sizeof(buff_drive));
    ZeroMemory(buff_dir,  sizeof(buff_dir));
    ZeroMemory(buff_name, sizeof(buff_name));
    ZeroMemory(buff_ext,  sizeof(buff_ext));

	_tsplitpath(m_strPath.c_str(), 
		pDrive     ? buff_drive : NULL,
		pDirectory ? buff_dir   : NULL,
		pName      ? buff_name  : NULL,
		pExtension ? buff_ext   : NULL);
                
    if(pDrive)
        *pDrive =buff_drive;
    if(pDirectory)
        *pDirectory =buff_dir;
    if(pName)
        *pName =buff_name;
    if(pExtension)
        *pExtension =buff_ext;

	// DOS's _splitpath returns "d:", we return "d"
	if(pDrive)
		StripTrailingChar(*pDrive,DRIVE_DELIMITER);
	// DOS's _splitpath returns "\dir\subdir\", we return "\dir\subdir"
	if(pDirectory)
		StripTrailingBackslash(*pDirectory);
	// DOS's _splitpath returns ".ext", we return "ext"	
	if(pExtension)
		StripLeadingChar(*pExtension,EXTENSION_DELIMITER);
}

//-------------------------------------------------------------
// Task    : Get drive from path
//-------------------------------------------------------------                       
void CPath::GetDrive(stdstr& rDrive) const
{
	GetComponents(&rDrive);
}

stdstr CPath::GetDrive(void) const
{
	stdstr rDrive;
	GetComponents(&rDrive);
	return rDrive;
}

//-------------------------------------------------------------
// Task    : Get drive and directory from path
//-------------------------------------------------------------
void CPath::GetDriveDirectory(stdstr& rDriveDirectory) const
{
    stdstr Drive;
    stdstr Directory;

	GetComponents(&Drive,&Directory);
	rDriveDirectory =Drive;
	if(!Drive.empty())
    {
		rDriveDirectory += DRIVE_DELIMITER;
        rDriveDirectory += Directory;
    }
}

stdstr CPath::GetDriveDirectory(void) const
{
	stdstr rDriveDirectory;
	GetDriveDirectory(rDriveDirectory);
	return rDriveDirectory;
}
//-------------------------------------------------------------
// Task    : Get directory from path
//-------------------------------------------------------------
void CPath::GetDirectory(stdstr& rDirectory) const
{
    GetComponents(NULL,&rDirectory);
}    

stdstr CPath::GetDirectory(void) const
{
	stdstr rDirectory;
	GetComponents(NULL,&rDirectory);
	return rDirectory;
}    

//-------------------------------------------------------------
// Task    : Get filename and extension from path
//-------------------------------------------------------------
void CPath::GetNameExtension(stdstr& rNameExtension) const
{
    stdstr Name;
    stdstr Extension;

	GetComponents(NULL,NULL,&Name,&Extension);
    rNameExtension =Name;
    if(!Extension.empty())
    {
    	rNameExtension += EXTENSION_DELIMITER;
        rNameExtension += Extension;
    }
}

stdstr CPath::GetNameExtension(void) const
{
	stdstr rNameExtension;
	GetNameExtension(rNameExtension);
	return rNameExtension;
}

//-------------------------------------------------------------
// Task    : Get filename from path
//-------------------------------------------------------------
void CPath::GetName(stdstr& rName) const
{
    GetComponents(NULL,NULL,&rName);
}

stdstr CPath::GetName(void) const
{
	stdstr rName;
	GetComponents(NULL,NULL,&rName);
	return rName;
}

//-------------------------------------------------------------
// Task    : Get file extension from path
//-------------------------------------------------------------
void CPath::GetExtension(stdstr& rExtension) const
{
    GetComponents(NULL,NULL,NULL,&rExtension);
}   

stdstr CPath::GetExtension(void) const
{
	stdstr rExtension;
	GetComponents(NULL,NULL,NULL,&rExtension);
	return rExtension;
}   

//-------------------------------------------------------------
// Task    : Get current directory
//-------------------------------------------------------------
void CPath::GetCurrentDirectory(stdstr& rDirectory) const
{
	stdstr Directory;

	rDirectory = "";
	
	GetDirectory(Directory);	
	StripTrailingBackslash(Directory);
	if(Directory.empty())
		return;
	
    stdstr::size_type nDelimiter =Directory.rfind(DIRECTORY_DELIMITER);
	
	rDirectory =Directory.substr(nDelimiter);
	StripLeadingBackslash(rDirectory);
}   

stdstr CPath::GetCurrentDirectory(void) const
{
	stdstr rDirecotry;
	GetCurrentDirectory(rDirecotry);
	return rDirecotry;
}   

//-------------------------------------------------------------
// Task    : Get fully qualified path
//-------------------------------------------------------------
void CPath::GetFullyQualified(stdstr& rFullyQualified) const
{
    TCHAR buff_fullname[MAX_PATH];

	memset(buff_fullname, 0, sizeof(buff_fullname));

	_tfullpath(buff_fullname,m_strPath.c_str(),MAX_PATH-1);
    rFullyQualified =buff_fullname;
}

//-------------------------------------------------------------
// Task    : Get fully qualified path in short (8.3 style) form
//-------------------------------------------------------------
void CPath::GetFullyQualifiedShort(stdstr& rFullyQualifiedShort) const
{
    GetFullyQualified(rFullyQualifiedShort);

	//#pragma message(Reminder(_T("Also implement a GetFullyQualifiedLong")))

    TCHAR buff_fullname[MAX_PATH];

	memset(buff_fullname, 0, sizeof(buff_fullname));

    GetShortPathName(rFullyQualifiedShort.c_str(),buff_fullname,sizeof(buff_fullname)/sizeof(TCHAR));
    rFullyQualifiedShort =buff_fullname;
}

//-------------------------------------------------------------
// Post    : Return TRUE if path does not start from filesystem root
// Task    : Check if path is a relative one (e.g. doesn't start with C:\...)
//-------------------------------------------------------------    
BOOL CPath::IsRelative() const
{
	if (m_strPath.length() > 1 && m_strPath[1] == DRIVE_DELIMITER)
	{
		return false;
	}
	if (m_strPath.length() > 1 && m_strPath[0] == DIRECTORY_DELIMITER && m_strPath[1] == DIRECTORY_DELIMITER)
	{
		return false;
	}
	return true;
}

//-------------------------------------------------------------
// Post    : Return TRUE if there are wildcards in the path
// Task    : Check if path contains wildcards
//-------------------------------------------------------------    
BOOL CPath::IsWild() const
{
    return (m_strPath.find_first_of(WILD_SET) != stdstr::npos);
}

//-------------------------------------------------------------
// Post    : Return TRUE if path is lexically correct
// Task    : Determine whether lpszFileName is valid. A filename
//           is valid if it contains only legal characters, doesn't
//           have repeated contiguous subdirectory delimiters, has at 
//           most one drive delimiter, and all components fit within 
//           maximum sizes
//           This routine does NOT determine if a file exists, or
//           even if it could exist relative to the user's directory
//           hierarchy. Its tests are for lexical correctness only
//           See also: CPath::Exists
//-------------------------------------------------------------
BOOL CPath::IsValid () const
{
    // Check 4 illegal characters (no wildcards allowed)
    // We accept either \ or / as folder delimiter
    if(IsWild() ||
       (m_strPath.find_first_of(_T("|\"<>")) != stdstr::npos) || 
       (m_strPath.find(_T("//")) != stdstr::npos))
        return FALSE;

    int index = (int)m_strPath.find(_T("\\\\"));
    if((index != stdstr::npos) && (index > 0))
        return FALSE;

    // Make sure : can appear only in the 2nd position as a drive delimiter
    if(((index = (int)m_strPath.find(':')) != stdstr::npos) && (index != 1))
        return FALSE;

    // Make sure it fits in the maximum path size
    if(m_strPath.length() > MAX_PATH)
        return FALSE;
    
    // Path is valid
	return TRUE;
}

//-------------------------------------------------------------
// Task    : Set path components
//-------------------------------------------------------------
void CPath::SetComponents(LPCTSTR lpszDrive, 
                          LPCTSTR lpszDirectory,
						  LPCTSTR lpszName, 
                          LPCTSTR lpszExtension)
{
    TCHAR buff_fullname[MAX_PATH];

	memset(buff_fullname, 0, sizeof(buff_fullname));

	_tmakepath(buff_fullname,lpszDrive,lpszDirectory,lpszName,lpszExtension);

    m_strPath.erase();
    m_strPath =buff_fullname;
}

//-------------------------------------------------------------
// Task    : Set path's drive
//-------------------------------------------------------------
void CPath::SetDrive(TCHAR chDrive)
{
	stdstr_f Drive(_T("%c"),chDrive);
	stdstr	 Directory;
	stdstr	 Name;
	stdstr	 Extension;
	
	GetComponents(NULL,&Directory,&Name,&Extension);
	SetComponents(Drive.c_str(),Directory.c_str(),Name.c_str(),Extension.c_str());
}

//-------------------------------------------------------------
// Task    : Set path's directory
//-------------------------------------------------------------
void CPath::SetDirectory(LPCTSTR lpszDirectory, BOOL bEnsureAbsolute /*= FALSE*/)
{
	stdstr	Drive;	
	stdstr	Directory =lpszDirectory;
	stdstr	Name;
	stdstr	Extension;
	
	if(bEnsureAbsolute)
		EnsureLeadingBackslash(Directory);
	EnsureTrailingBackslash(Directory);

	GetComponents(&Drive,NULL,&Name,&Extension);
	SetComponents(Drive.c_str(),Directory.c_str(),Name.c_str(),Extension.c_str());
}    

//-------------------------------------------------------------
// Task    : Set path's drive and directory
//-------------------------------------------------------------
void CPath::SetDriveDirectory(LPCTSTR lpszDriveDirectory)
{
	stdstr	DriveDirectory =lpszDriveDirectory;
	stdstr	Name;
	stdstr	Extension;
	
	EnsureTrailingBackslash(DriveDirectory);
	cleanPathString(DriveDirectory);
	
	GetComponents(NULL,NULL,&Name,&Extension);
	SetComponents(NULL,DriveDirectory.c_str(),Name.c_str(),Extension.c_str());
}    

//-------------------------------------------------------------
// Task    : Set path's filename
//-------------------------------------------------------------
void CPath::SetName(LPCTSTR lpszName)
{
	stdstr	Drive;
	stdstr	Directory;
	stdstr	Extension;
	
	GetComponents(&Drive,&Directory,NULL,&Extension);
	SetComponents(Drive.c_str(),Directory.c_str(),lpszName,Extension.c_str());
}

//-------------------------------------------------------------
// Task    : Set path's filename
//-------------------------------------------------------------
void CPath::SetName(int iName)
{
	stdstr	Drive;
	stdstr	Directory;
	stdstr	Extension;
	TCHAR 	sName[33];
	
	memset(sName, 0, sizeof(sName));

	_itot(iName, sName, 10);
	
	GetComponents(&Drive,&Directory,NULL,&Extension);
	SetComponents(Drive.c_str(),Directory.c_str(),sName,Extension.c_str());
}


//-------------------------------------------------------------
// Task    : Set path's file extension
//-------------------------------------------------------------
void CPath::SetExtension(LPCTSTR lpszExtension)
{
	stdstr	Drive;
	stdstr	Directory;
	stdstr	Name;
	
	GetComponents(&Drive,&Directory,&Name);
	SetComponents(Drive.c_str(),Directory.c_str(),Name.c_str(),lpszExtension);
}

//-------------------------------------------------------------
// Task    : Set path's file extension
//-------------------------------------------------------------
void CPath::SetExtension(int iExtension)
{
	stdstr	Drive;
	stdstr	Directory;
	stdstr	Name;
	TCHAR	sExtension[20];

	memset(sExtension, 0, sizeof(sExtension));

	_itot(iExtension, sExtension, 10);

	GetComponents(&Drive,&Directory,&Name);
	SetComponents(Drive.c_str(),Directory.c_str(),Name.c_str(),sExtension);
}

//-------------------------------------------------------------
// Task    : Set path's filename and extension
//-------------------------------------------------------------
void CPath::SetNameExtension(LPCTSTR lpszNameExtension)
{
	stdstr	Drive;
	stdstr	Directory;

	GetComponents(&Drive,&Directory);
	SetComponents(Drive.c_str(),Directory.c_str(),lpszNameExtension,NULL);
}    

//-------------------------------------------------------------
// Task    : Append a subdirectory 2 path's directory
//-------------------------------------------------------------
void CPath::AppendDirectory(LPCTSTR lpszSubDirectory)
{                                               
	stdstr	Drive;
	stdstr	Directory;
	stdstr	SubDirectory =lpszSubDirectory;
	stdstr	Name;
	stdstr	Extension;
	
	if(SubDirectory.empty())
		return;

	// Strip out any preceeding backslash
	StripLeadingBackslash(SubDirectory);
	EnsureTrailingBackslash(SubDirectory);

	GetComponents(&Drive,&Directory,&Name,&Extension);
	EnsureTrailingBackslash(Directory);
    Directory +=SubDirectory;

	SetComponents(Drive.c_str(),Directory.c_str(),Name.c_str(),Extension.c_str());
}

//-------------------------------------------------------------
// Pre     : If pLastDirectory is given we will store the name of the
//           deepest directory (the one we're just exiting) in it
// Task    : Remove deepest subdirectory from path
//-------------------------------------------------------------
void CPath::UpDirectory(stdstr *pLastDirectory /*= NULL*/)
{
	stdstr Directory;

	GetDirectory(Directory);	
	StripTrailingBackslash(Directory);
	if(Directory.empty())
		return;
	
    stdstr::size_type nDelimiter =Directory.rfind(DIRECTORY_DELIMITER);
	
	if(pLastDirectory != NULL)
	{
		*pLastDirectory =Directory.substr(nDelimiter);
		StripLeadingBackslash(*pLastDirectory);
	}
		
    if(nDelimiter != stdstr::npos)
		Directory =Directory.substr(0,nDelimiter);
		
	SetDirectory(Directory.c_str());
}

//-------------------------------------------------------------
// Task    : Set path 2 current directory
//-------------------------------------------------------------
void CPath::CurrentDirectory()
{
	TCHAR buff_path[MAX_PATH];
	
	memset(buff_path, 0, sizeof(buff_path));

    ::GetCurrentDirectory(MAX_PATH,buff_path);
	
	Empty();
	SetDriveDirectory(buff_path);
}

//-------------------------------------------------------------
// Task    : Set path 2 Windows directory
//-------------------------------------------------------------
void CPath::WindowsDirectory()
{
	TCHAR buff_path[MAX_PATH];
	
	memset(buff_path, 0, sizeof(buff_path));

	GetWindowsDirectory(buff_path,MAX_PATH);
    
    Empty();
    SetDriveDirectory(buff_path);
}

//-------------------------------------------------------------
// Task    : Set path 2 Windows system directory
//-------------------------------------------------------------
void CPath::SystemDirectory()
{
	TCHAR buff_path[MAX_PATH];
	
	memset(buff_path, 0, sizeof(buff_path));

	GetSystemDirectory(buff_path,MAX_PATH);
    
    Empty();
    SetDriveDirectory(buff_path);
}

//-------------------------------------------------------------
// Task    : Set path 2 root of system drive (usually C:\)
//-------------------------------------------------------------
void CPath::SystemDriveRootDirectory()
{
    SystemDirectory();
    SetDirectory(_T(""));
}

//-------------------------------------------------------------
// Task    : Set path 2 the name of specified module
//-------------------------------------------------------------
void CPath::Module(HINSTANCE hInstance)
{
    TCHAR buff_path[MAX_PATH];

	memset(buff_path, 0, sizeof(buff_path));

	GetModuleFileName(hInstance,buff_path,MAX_PATH);
    m_strPath =buff_path;
}

//-------------------------------------------------------------
// Task    : Set path 2 the name of current module
//-------------------------------------------------------------
void CPath::Module()
{
    TCHAR buff_path[MAX_PATH];
	memset(buff_path, 0, sizeof(buff_path));

	GetModuleFileName(m_hInst,buff_path,MAX_PATH);
    m_strPath =buff_path;
}

//-------------------------------------------------------------
// Task    : Set path 2 the directory of specified module
//-------------------------------------------------------------
void CPath::ModuleDirectory(HINSTANCE hInstance)
{
	Module(hInstance);
	SetNameExtension(_T(""));
}

//-------------------------------------------------------------
// Task    : Set path 2 the directory of current module
//-------------------------------------------------------------
void CPath::ModuleDirectory()
{
	Module();
	SetNameExtension(_T(""));
}

//-------------------------------------------------------------
// Task    : Currently, if the current environment has an
//           entry for the TEMP environment variable, the directory will
//           be set to that. If not, the directory will be the Windows
//           System directory. The caller of this method, however, should
//           not rely on this convention
//-------------------------------------------------------------
void CPath::TempDirectory()
{
    TCHAR buff_path[MAX_PATH];

	memset(buff_path, 0, sizeof(buff_path));

	GetTempPath(MAX_PATH,buff_path);

	m_strPath =buff_path;
	SetNameExtension(_T(""));	
}						 

//-------------------------------------------------------------
// Task    : Set path 2 program files folder
//           Usually C:\Program Files
//-------------------------------------------------------------
void CPath::ProgramFilesDirectory()
{
    stdstr strPath;
    if(GetRegistryPath(HKEY_LOCAL_MACHINE,_T("SOFTWARE\\Microsoft\\Windows\\CurrentVersion"),_T("ProgramFilesDir"),strPath))
    {
        // Got a path, use it
        Empty();
        SetDriveDirectory(strPath.c_str());
    }
    else
    {
        // This is some old or unknown system
        Empty();
        SetDriveDirectory(_T("C:\\Programs"));
    }
}

//-------------------------------------------------------------
// Task    : Set path 2 common files folder
//           Usually C:\Program Files\Common Files
//-------------------------------------------------------------
void CPath::CommonFilesDirectory()
{
    stdstr strPath;
    if(GetRegistryPath(HKEY_LOCAL_MACHINE,_T("SOFTWARE\\Microsoft\\Windows\\CurrentVersion"),_T("CommonFilesDir"),strPath))
    {
        // Got a path, use it
        Empty();
        SetDriveDirectory(strPath.c_str());
    }
    else
    {
		// This is some old or unknown system
		Empty();

		TCHAR Drive[3];
		memset(Drive, 0, sizeof(Drive));
		ExpandEnvironmentStrings (_T("%SystemDrive%"), Drive, sizeof(Drive)/sizeof(TCHAR) );

		stdstr strDir = stdstr(Drive) + stdstr(_T("\\Program Files\\Common Files"));

		SetDriveDirectory(strDir.c_str());
    }
}

//-------------------------------------------------------------
// Task    : Set path 2 common files folder
//           On Win95 is C:\Program Files\Accessories
//           On WinNT is C:\Program Files\Windows NT\Accessories
//-------------------------------------------------------------
void CPath::AccessoriesDirectory()
{
    // Accessories folder is in Program Files folder
    ProgramFilesDirectory();

    COSVersion osver;
    WORD       ostype   =osver.GetOSType();
    WORD       wintype  =osver.GetWindowsType();
    BOOL       is_Win95 =(ostype==OS_WIN95) || (ostype==OS_WIN98);
    BOOL       is_NT    =((ostype & OS_WINNT) != 0);

    if((wintype != WIN_32S) && is_Win95)
    {
        // Windows 95
        AppendDirectory(_T("Accessories"));
        return;
    }

    if((wintype != WIN_32S) && is_NT && (((osver.GetMajorVersion()==3) && (osver.GetMinorVersion()>=51)) || (osver.GetMajorVersion()>3)))
    {
        // Windows NT with the new Chichago shell
        AppendDirectory(_T("Windows NT\\Accessories"));
        return;
    }

    // This is some old or unknown system
    AppendDirectory(_T("Accessry"));
}

//-------------------------------------------------------------
// Task    : Set path 2 media folder
//           Usually C:\Windows\Media
//-------------------------------------------------------------
void CPath::MediaDirectory()
{
    stdstr strPath;
    if(GetRegistryPath(HKEY_LOCAL_MACHINE,_T("SOFTWARE\\Microsoft\\Windows\\CurrentVersion"),_T("MediaPath"),strPath))
    {
        // Got a path, use it
        Empty();
        SetDriveDirectory(strPath.c_str());
    }
    else
    {
        // This is some old or unknown system
        WindowsDirectory();
        AppendDirectory(_T("Media"));
    }
}

//-------------------------------------------------------------
// Task    : Set path 2 device definition folder
//           Usually C:\Windows\Inf
//-------------------------------------------------------------
void CPath::DeviceDirectory()
{
    stdstr strPath;
    if(GetRegistryPath(HKEY_LOCAL_MACHINE,_T("SOFTWARE\\Microsoft\\Windows\\CurrentVersion"),_T("DevicePath"),strPath))
    {
        // Got a path, use it
        Empty();
        SetDriveDirectory(strPath.c_str());
    }
    else
    {
        // This is some old or unknown system
        WindowsDirectory();
        AppendDirectory(_T("Inf"));
    }
}

//-------------------------------------------------------------
// Post    : If this function is called on a system which does not
//           support the new Chichago shell, it will return FALSE
// Task    : Set path 2 one of the special folders in Chichago
//-------------------------------------------------------------
BOOL CPath::ShellDirectory(int nShellFolderID)
{
    COSVersion osver;
    WORD       ostype   =osver.GetOSType();
    WORD       wintype  =osver.GetWindowsType();
    BOOL       is_Win95 =(ostype==OS_WIN95) || (ostype==OS_WIN98);
    BOOL       is_NT    =((ostype & OS_WINNT) != 0);

    if((wintype != WIN_32S) && 
       (is_Win95 || (is_NT && (((osver.GetMajorVersion()==3) && (osver.GetMinorVersion()>=51)) || (osver.GetMajorVersion()>3)))))
    {
        // These systems support the new Chichago shell, get location from registry
        BOOL         result =FALSE;
        LPITEMIDLIST pidl   =NULL;
        TCHAR        special_path[MAX_PATH];

		memset(special_path, 0, sizeof(special_path));

		// Get a PIDL 2 the special shell folder
        HRESULT hr =SHGetSpecialFolderLocation(NULL,nShellFolderID,&pidl);
        if(SUCCEEDED(hr))
        {
            // Convert the PIDL in2 a path
            result =SHGetPathFromIDList(pidl,special_path);

            // Free the PIDL
            // Get the address of our task allocator's IMalloc interface
            LPMALLOC pMalloc = NULL;
            hr =SHGetMalloc(&pMalloc);

            if(SUCCEEDED(hr))
            {
                // Free the PIDL
                pMalloc->Free(pidl);
            
                // Free our task allocator
                pMalloc->Release();
            }
        }

        if(result)
        {
            // We've got the special path, now set ourselves 2 point 2 this
            Empty();
            SetDriveDirectory(special_path);
        }

        return result;
    }

    // This is some old or unknown system, shell folders not supported
    return FALSE;
}

//---------------------------------------------------------------------------
// Post    : Return TRUE on success
// Task    : Set path 2 one of the special folders in Chichago
//           This function manually digs in the registry instead of using
//           SHGetSpecialFolderLocation, since it seems that this does not work 4
//           special location constants beginning with 
//---------------------------------------------------------------------------
BOOL CPath::ShellDirectory2(int nShellFolderID)
{
    COSVersion osver;
    WORD       ostype   =osver.GetOSType();
    WORD       wintype  =osver.GetWindowsType();
    BOOL       is_Win95 =(ostype==OS_WIN95) || (ostype==OS_WIN98);
    BOOL       is_NT    =((ostype & OS_WINNT) != 0);

    if((wintype != WIN_32S) && 
       (is_Win95 || (is_NT && (((osver.GetMajorVersion()==3) && (osver.GetMinorVersion()>=51)) || (osver.GetMajorVersion()>3)))))
    {
        // These systems support the new Chichago shell, get location from registry
        HKEY      root = NULL;
        stdstr   key;
        stdstr   value;

        switch(nShellFolderID)
        {
            case CSIDL_DESKTOPDIRECTORY:
                root  =HKEY_CURRENT_USER;
                key   =_T("Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\Shell Folders");
                value =_T("Desktop");
                break;

            case CSIDL_FAVORITES:
                root  =HKEY_CURRENT_USER;
                key   =_T("Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\Shell Folders");
                value =_T("Favorites");
                break;

            case CSIDL_FONTS:
                root  =HKEY_CURRENT_USER;
                key   =_T("Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\Shell Folders");
                value =_T("Fonts");
                break;

            case CSIDL_NETHOOD:
                root  =HKEY_CURRENT_USER;
                key   =_T("Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\Shell Folders");
                value =_T("NetHood");
                break;

            case CSIDL_PERSONAL:
                root  =HKEY_CURRENT_USER;
                key   =_T("Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\Shell Folders");
                value =_T("Personal");
                break;

            case CSIDL_RECENT:
                root  =HKEY_CURRENT_USER;
                key   =_T("Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\Shell Folders");
                value =_T("Recent");
                break;

            case CSIDL_SENDTO:
                root  =HKEY_CURRENT_USER;
                key   =_T("Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\Shell Folders");
                value =_T("SendTo");
                break;

            case CSIDL_TEMPLATES:
                root  =HKEY_CURRENT_USER;
                key   =_T("Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\Shell Folders");
                value =_T("Templates");
                break;

            case CSIDL_APPDATA:
                root  =HKEY_CURRENT_USER;
                key   =_T("Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\Shell Folders");
                value =_T("AppData");
                break;

            case CSIDL_STARTMENU:
                root  =HKEY_CURRENT_USER;
                key   =_T("Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\Shell Folders");
                value =_T("Start Menu");
                break;

            case CSIDL_STARTUP:
                root  =HKEY_CURRENT_USER;
                key   =_T("Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\Shell Folders");
                value =_T("Startup");
                break;

            case CSIDL_PROGRAMS:
                root  =HKEY_CURRENT_USER;
                key   =_T("Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\Shell Folders");
                value =_T("Programs");
                break;

            case CSIDL_COMMON_DESKTOPDIRECTORY:
                root  =HKEY_LOCAL_MACHINE;
                key   =_T("SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Explorer\\Shell Folders");
                value =_T("Common Desktop");
                break;

            case CSIDL_COMMON_STARTMENU:
                root  =HKEY_LOCAL_MACHINE;
                key   =_T("SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Explorer\\Shell Folders");
                value =_T("Common Start Menu");
                break;

            case CSIDL_COMMON_STARTUP:
                root  =HKEY_LOCAL_MACHINE;
                key   =_T("SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Explorer\\Shell Folders");
                value =_T("Common Startup");
                break;

            case CSIDL_COMMON_PROGRAMS:
                root  =HKEY_LOCAL_MACHINE;
                key   =_T("SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Explorer\\Shell Folders");
                value =_T("Common Programs");
                break;
        }
        
        stdstr strPath;
        if(GetRegistryPath(root,key.c_str(),value.c_str(),strPath))
        {
            // Got a path, use it
            Empty();
            SetDriveDirectory(strPath.c_str());

            return TRUE;
        }
    }

    // This is some old or unknown system
    return FALSE;
}

//---------------------------------------------------------------------------
// Post    : Return FALSE if specified value does not exist or some error
// Task    : Dig in the registry 2 the specified location, and extract a path
//           Make sure the path is a valid 
//---------------------------------------------------------------------------
BOOL CPath::GetRegistryPath(HKEY hRootKey, LPCTSTR lpcszKeyName, LPCTSTR lpcszValueName, stdstr &strPath)
{
    TCHAR     path_buffer    [MAX_PATH];
    TCHAR     expanded_buffer[MAX_PATH];
    ULONG     path_buffer_size =sizeof(path_buffer);
    CRegistry reg(hRootKey,lpcszKeyName,KEY_READ);
	memset(path_buffer, 0, sizeof(path_buffer));
	memset(expanded_buffer, 0, sizeof(expanded_buffer));

	if(reg.GetValue(lpcszValueName,(BYTE *)&path_buffer,path_buffer_size))
    {
        COSVersion osver;
#ifndef _UNICODE
		WORD       ostype =osver.GetOSType();
		BOOL       is_NT  =((ostype & OS_WINNT) != 0);
       if(is_NT)
        {
            // Running on NT and the ExpandEnvironmentStrings API requires
            // Unicode strings
            WCHAR path_buffer_unicode    [MAX_PATH];
            WCHAR expanded_buffer_unicode[MAX_PATH];

			memset(path_buffer_unicode, 0, sizeof(path_buffer_unicode));
			memset(expanded_buffer_unicode, 0, sizeof(expanded_buffer_unicode));

			MultiByteToWideChar(CP_ACP,0,path_buffer,-1,path_buffer_unicode,sizeof(path_buffer_unicode)/sizeof(WCHAR));
            ExpandEnvironmentStringsW(path_buffer_unicode,expanded_buffer_unicode,sizeof(expanded_buffer_unicode)/sizeof(WCHAR));
            WideCharToMultiByte(CP_ACP,0,expanded_buffer_unicode,-1,expanded_buffer,sizeof(path_buffer)/sizeof(TCHAR),NULL,NULL);
        }
        else
#endif
            ExpandEnvironmentStrings(path_buffer,expanded_buffer,path_buffer_size);

        strPath.erase();
        strPath =expanded_buffer;

        return TRUE;
    }

    // No such key and/or value
    return FALSE;
}

//-------------------------------------------------------------
// Post    : If this function is called on a system which does not
//           support the new Chichago shell, it will clear the path
// Task    : Set path 2 desktop folder of currently logged-in user
//-------------------------------------------------------------
void CPath::UserDesktopDirectory()
{
    if(!ShellDirectory(CSIDL_DESKTOPDIRECTORY))
        Empty();
}

//-------------------------------------------------------------
// Post    : If this function is called on a system which does not
//           support the new Chichago shell, it will clear the path
// Task    : Set path 2 favorites folder of currently logged-in user
//-------------------------------------------------------------
void CPath::UserFavoritesDirectory()
{
    if(!ShellDirectory(CSIDL_FAVORITES))
        Empty();
}

//-------------------------------------------------------------
// Post    : If this function is called on a system which does not
//           support the new Chichago shell, it will clear the path
// Task    : Set path 2 fonts folder of currently logged-in user
//-------------------------------------------------------------
void CPath::UserFontsDirectory()
{
    if(!ShellDirectory(CSIDL_FONTS))
        Empty();
}

//-------------------------------------------------------------
// Post    : If this function is called on a system which does not
//           support the new Chichago shell, it will clear the path
// Task    : Set path 2 network hood folder of currently logged-in user
//-------------------------------------------------------------
void CPath::UserNetworkNeighbourhoodDirectory()
{
    if(!ShellDirectory(CSIDL_NETHOOD))
        Empty();
}

//-------------------------------------------------------------
// Post    : If this function is called on a system which does not
//           support the new Chichago shell, it will clear the path
// Task    : Set path 2 personal folder of currently logged-in user
//           Usually C:\My Documents
//-------------------------------------------------------------
void CPath::UserDocumentsDirectory()
{
    if(!ShellDirectory(CSIDL_PERSONAL))
        Empty();
}

//-------------------------------------------------------------
// Post    : If this function is called on a system which does not
//           support the new Chichago shell, it will clear the path
// Task    : Set path 2 recent folder of currently logged-in user
//-------------------------------------------------------------
void CPath::UserRecentDirectory()
{
    if(!ShellDirectory(CSIDL_RECENT))
        Empty();
}

//-------------------------------------------------------------
// Post    : If this function is called on a system which does not
//           support the new Chichago shell, it will clear the path
// Task    : Set path 2 SendTo folder of currently logged-in user
//-------------------------------------------------------------
void CPath::UserSendToDirectory()
{
    if(!ShellDirectory(CSIDL_SENDTO))
        Empty();
}

//-------------------------------------------------------------
// Post    : If this function is called on a system which does not
//           support the new Chichago shell, it will clear the path
// Task    : Set path 2 templates folder of currently logged-in user
//           Usually C:\Windows\ShellNew
//-------------------------------------------------------------
void CPath::UserTemplatesDirectory()
{
    if(!ShellDirectory(CSIDL_TEMPLATES))
        Empty();
}

//-------------------------------------------------------------
// Post    : If this function is called on a system which does not
//           support the new Chichago shell, it will clear the path
// Task    : Set path 2 the recycle bin directory
//           Usually C:\Recycled
//-------------------------------------------------------------
void CPath::UserRecycleBinDirectory()
{
    if(!ShellDirectory(CSIDL_BITBUCKET))
        Empty();
}

//-------------------------------------------------------------
// Post    : If this function is called on a system which does not
//           support the new Chichago shell, it will clear the path
// Task    : Set path 2 the folder where application data is stored 
//           specific 2 currently logged-in user
//-------------------------------------------------------------
void CPath::UserApplicationDataDirectory()
{
    if(!ShellDirectory(CSIDL_APPDATA))
        Empty();
}

//-------------------------------------------------------------
// Post    : If this function is called on a system which does not
//           support the new Chichago shell, it will clear the path
// Task    : Set path 2 start menu folder of currently logged-in user
//-------------------------------------------------------------
void CPath::UserStartMenuDirectory()
{
    if(!ShellDirectory(CSIDL_STARTMENU))
        Empty();
}

//-------------------------------------------------------------
// Post    : If this function is called on a system which does not
//           support the new Chichago shell, it will clear the path
// Task    : Set path 2 startup folder of currently logged-in user
//-------------------------------------------------------------
void CPath::UserStartMenuStartupDirectory()
{
    if(!ShellDirectory(CSIDL_STARTUP))
        Empty();
}

//-------------------------------------------------------------
// Post    : If this function is called on a system which does not
//           support the new Chichago shell, it will clear the path
// Task    : Set path 2 programs menu folder of currently logged-in user
//-------------------------------------------------------------
void CPath::UserStartMenuProgramsDirectory()
{
    if(!ShellDirectory(CSIDL_PROGRAMS))
        Empty();
}

//-------------------------------------------------------------
// Post    : If this function is called on a system which does not
//           support the new Chichago shell, it will clear the path
// Task    : Set path 2 desktop folder common 2 all users
//-------------------------------------------------------------
void CPath::CommonDesktopDirectory()
{
    if(!ShellDirectory2(CSIDL_COMMON_DESKTOPDIRECTORY))
    {
        // Check if running on Windows 95, and workaround this if so
        COSVersion osver;
        WORD       ostype =osver.GetOSType();
        if((ostype == OS_WIN95) || (ostype == OS_WIN98))
        {
            // Manual workaround
            WindowsDirectory();
            AppendDirectory(_T("Desktop"));
        }
        else
            // Failure, clear path
            Empty();
    }
}

//-------------------------------------------------------------
// Post    : If this function is called on a system which does not
//           support the new Chichago shell, it will clear the path
// Task    : Set path 2 start menu folder common 2 all users
//-------------------------------------------------------------
void CPath::CommonStartMenuDirectory()
{
    if(!ShellDirectory2(CSIDL_COMMON_STARTMENU))
    {
        // Check if running on Windows 95, and workaround this if so
        COSVersion osver;
        WORD       ostype =osver.GetOSType();
        if((ostype == OS_WIN95) || (ostype == OS_WIN98))
        {
            // Manual workaround
            WindowsDirectory();
            AppendDirectory(_T("Start Menu"));
        }
        else
            // Failure, clear path
            Empty();
    }
}

//-------------------------------------------------------------
// Post    : If this function is called on a system which does not
//           support the new Chichago shell, it will clear the path
// Task    : Set path 2 startup folder common 2 all users
//-------------------------------------------------------------
void CPath::CommonStartMenuStartupDirectory()
{
    if(!ShellDirectory2(CSIDL_COMMON_STARTUP))
    {
        // Check if running on Windows 95, and workaround this if so
        COSVersion osver;
        WORD       ostype =osver.GetOSType();
        if((ostype == OS_WIN95) || (ostype == OS_WIN98))
        {
            // Manual workaround
            WindowsDirectory();
            AppendDirectory(_T("Start Menu\\Programs\\StartUp"));
        }
        else
            // Failure, clear path
            Empty();
    }
}

//-------------------------------------------------------------
// Post    : If this function is called on a system which does not
//           support the new Chichago shell, it will clear the path
// Task    : Set path 2 programs menu folder common 2 all users
//-------------------------------------------------------------
void CPath::CommonStartMenuProgramsDirectory()
{
    if(!ShellDirectory2(CSIDL_COMMON_PROGRAMS))
    {
        // Check if running on Windows 95, and workaround this if so
        COSVersion osver;
        WORD       ostype =osver.GetOSType();
        if((ostype == OS_WIN95) || (ostype == OS_WIN98))
        {
            // Manual workaround
            WindowsDirectory();
            AppendDirectory(_T("Start Menu\\Programs"));
        }
        else
            // Failure, clear path
            Empty();
    }
}


//-------------------------------------------------------------
// Task    : Set path 2 file WIN.INI in the Windows directory
//-------------------------------------------------------------
void CPath::WindowsProfile()
{
    WindowsDirectory();
	SetNameExtension(_T("Win.INI"));
}

//-------------------------------------------------------------
// Task    : Set path 2 file WIN.INI in the Windows directory
//-------------------------------------------------------------
void CPath::SystemProfile()
{
    WindowsDirectory();
	SetNameExtension(_T("System.INI"));
}

//-------------------------------------------------------------
// Task    : Turn this path from "x:\directory\subdirectory\name.ext"
//           to just "x:\"
//-------------------------------------------------------------
void CPath::MakeRoot()
{   
	SetDirectory(_T(""));
	SetNameExtension(_T(""));
}

//-------------------------------------------------------------
// Pre     : Only the first 3 character from lpcszPrefix will be used
// Post    : Returns TRUE on success
// Task    : Creates a temporary name
//-------------------------------------------------------------
BOOL CPath::CreateTempName(LPCTSTR lpcszPrefix)
{
    // Check that we've got a prefix
    if(!lpcszPrefix)
        return FALSE;

	stdstr Dir;
    TCHAR  temp_file[MAX_PATH];

	memset(temp_file, 0, sizeof(temp_file));

	GetDriveDirectory(Dir);

    if(::GetTempFileName(Dir.c_str(),lpcszPrefix,0,temp_file) != 0)
    {
        // Got a temp file name
        *this =temp_file;
        SetExtension(_T("tmp"));

        // GetTempFileName actually created the file, remove it now,
        // we only needed a name
        Delete(TRUE);

        return TRUE;
    }

	return FALSE;
}

//---------------------------------------------------------------------------
// Pre     : Only the first 3 character from lpcszPrefix will be used
// Post    : Returns TRUE on success
// Task    : Creates a temporary folder name
//---------------------------------------------------------------------------
BOOL CPath::CreateTempDir(LPCTSTR lpcszPrefix, UINT nRetries)
{
    // Check that we've got a prefix
    if(!lpcszPrefix)
        return FALSE;

    UINT  retries =0;
    BOOL  bSuccess =FALSE;
	TCHAR temp_prefix[ 5];
    TCHAR temp_name  [15];

	ZeroMemory(temp_prefix, sizeof(temp_prefix));
	ZeroMemory(temp_name , sizeof(temp_name ));
	_tcsncpy(temp_prefix,lpcszPrefix,4);
    temp_prefix[3] =_T('\0');

    while(!bSuccess && (retries < nRetries))
    {
        _tcscpy(temp_name, temp_prefix);
        stdstr temp =RandomDigits(5);
        _tcscat(temp_name,temp.c_str());
        _tcscat(temp_name,_T(".tmp"));

        CPath test(*this);

        test.AppendDirectory(temp_name);
        if(!test.DirectoryExists() && test.CreateDirectory())
        {
            // CreateTempDir actually created the folder, remove it now,
            // we only needed a name
            test.RemoveDirectory();
            bSuccess =TRUE;
        }

        retries++;
    }

    if(bSuccess)
        AppendDirectory(temp_name);

	return bSuccess;
}

//-------------------------------------------------------------
// Post    : Returns TRUE on success
// Task    : Sets path 2 a random name, and optionally ensures
//           uniqueness of that path
//-------------------------------------------------------------
BOOL CPath::CreateRandomName(BOOL bMustNotExist /*= TRUE*/, UINT nRetries /*= 1000*/)
{
	stdstr Name;

	for(UINT nRetry=0; nRetry < nRetries; nRetry++)
	{
		Name =RandomDigits(8);
		SetName(Name.c_str());
        if(!bMustNotExist)
            return TRUE;
		if(!Exists())
			return TRUE;
	}

	return FALSE;
}

//-------------------------------------------------------------
// Post    : Returns TRUE on success
// Task    : Create a new name, based on the existing name, for the same
//           drive and directory. If bMustNotExist, test the path up to 
//           nRetries till we get an unused path
//           See also: CreateRandomName
//-------------------------------------------------------------
BOOL CPath::CreateSimilarName(BOOL bMustNotExist /*= TRUE*/, UINT nRetries /*= 1000*/)
{
	stdstr NewName;
    stdstr OriginalName;

	GetName(OriginalName);
		
	for(UINT nRetry=0; nRetry < nRetries; nRetry++)
	{
		NewName =OriginalName + RandomDigits(_MAX_FNAME - (int)OriginalName.length());
		SetName(NewName.c_str());
		if(!Exists() || !bMustNotExist)
			return TRUE;
	}

	return FALSE;
}

//-------------------------------------------------------------
// Post    : Returns one of the EX_DRIVE_ constants
// Task    : Return the type of the drive this path points to
//           See DrvType.H for more details
//-------------------------------------------------------------
UINT CPath::GetDriveType() const
{
	CPath  RootPath = *this;
    stdstr Root;

	RootPath.MakeRoot();
    Root =(LPCTSTR)RootPath;

	return ::GetDriveType(Root.c_str());
}

//-------------------------------------------------------------
// Post    : Return -1 on error
// Task    : Find out the amount of free space on drive (in bytes)
//-------------------------------------------------------------
ULONG CPath::DriveFreeSpaceBytes() const
{
    CPath  RootPath = *this;
    stdstr Root;

	RootPath.MakeRoot();

	ULONG nSectorsPerCluster = 0;
	ULONG nBytesPerSector = 0;
	ULONG nFreeClusters = 0;
	ULONG nClusters = 0;

	if(!GetDiskFreeSpace((LPCTSTR)RootPath,&nSectorsPerCluster,&nBytesPerSector,&nFreeClusters,&nClusters))
		return 0;
	else		
		return nFreeClusters * nSectorsPerCluster * nBytesPerSector;
}

//-------------------------------------------------------------
// Post    : Return -1 on error
// Task    : Find out the size of the drive (in bytes)
//-------------------------------------------------------------
ULONG CPath::DriveTotalSpaceBytes() const
{
    CPath  RootPath = *this;
    stdstr Root;

	RootPath.MakeRoot();

	ULONG nSectorsPerCluster = 0;
	ULONG nBytesPerSector = 0;
	ULONG nFreeClusters = 0;
	ULONG nClusters = 0;

	if(!GetDiskFreeSpace((LPCTSTR)RootPath,&nSectorsPerCluster,&nBytesPerSector,&nFreeClusters,&nClusters))
		return 0;
	else		
		return nClusters * nSectorsPerCluster * nBytesPerSector;
}

//-------------------------------------------------------------
// Post    : Return -1 on error
// Task    : Find out the cluster size on this drive (in bytes)
//-------------------------------------------------------------
ULONG CPath::GetDriveClusterSize() const
{
    CPath  RootPath = *this;
    stdstr Root;

	RootPath.MakeRoot();

	ULONG nSectorsPerCluster = 0;
	ULONG nBytesPerSector = 0;
	ULONG nFreeClusters = 0;
	ULONG nClusters = 0;

	if(!GetDiskFreeSpace((LPCTSTR)RootPath,&nSectorsPerCluster,&nBytesPerSector,&nFreeClusters,&nClusters))
		return 0;
	else		
		return nSectorsPerCluster * nBytesPerSector;
}

//-------------------------------------------------------------
// Post    : Return TRUE on success
// Task    : Find out info about drive
//-------------------------------------------------------------
BOOL CPath::GetDiskInfo(LPDWORD lpSectorsPerCluster,
						LPDWORD lpBytesPerSector,
						LPDWORD lpFreeClusters,
						LPDWORD lpClusters) const
{
    // Create root path
	CPath RootPath = *this;
	RootPath.MakeRoot();

	return GetDiskFreeSpace((LPCTSTR)RootPath,
                            lpSectorsPerCluster,
                            lpBytesPerSector,
                            lpFreeClusters,
                            lpClusters);
}

//---------------------------------------------------------------------------
// Post    : Return TRUE if a directory
// Task    : Check if this path represents a directory
//---------------------------------------------------------------------------
BOOL CPath::IsDirectory() const
{
    // Check if this path has a filename
    stdstr file_name;
    GetNameExtension(file_name);
    
    return file_name.empty();
}

//-------------------------------------------------------------
// Post    : Return TRUE if directory exists
// Task    : To determine if the directory exists, we need to
//           create a test path with a wildcard (*.*) extension
//           and see if FindFirstFile returns anything.  We don't
//           use CPath::FindFirst() because that routine parses out
//           '.' and '..', which fails for empty directories
//-------------------------------------------------------------
BOOL CPath::DirectoryExists() const
{
    // Create test path	
	CPath TestPath(m_strPath.c_str());

	stdstr DirName;
	TestPath.UpDirectory(&DirName);
	TestPath.SetNameExtension(DirName.c_str());

	WIN32_FIND_DATA	FindData;
	HANDLE          hFindFile =FindFirstFile((LPCTSTR)TestPath,&FindData); // Find anything
	BOOL            bGotFile  =(hFindFile != INVALID_HANDLE_VALUE);

	if(hFindFile != NULL)	// Make sure we close the search
	    FindClose(hFindFile);

	return bGotFile;
}                                                     

//-------------------------------------------------------------
// Post    : Return TRUE if there are no files(s)/folder(s) in
//           directory. All objects (with hidden, system, etc. attributes)
//           are looked up
// Task    : Check if directory contains any file(s)
//-------------------------------------------------------------	
BOOL CPath::IsDirectoryEmpty() const
{
	CPath FileSpec = *this;
	
	FileSpec.SetNameExtension(WILD_NAME_EXTENSION);
	return !FileSpec.FindFirst(_A_NORMAL | _A_ARCH | _A_HIDDEN | _A_SYSTEM | _A_RDONLY) &&
           !FileSpec.FindFirst(_A_HIDDEN | _A_SUBDIR);
}

//-------------------------------------------------------------
// Post    : Return TRUE if these is such a file
// Task    : Check if file exists
//-------------------------------------------------------------
BOOL CPath::Exists() const
{
	WIN32_FIND_DATA FindData;
	HANDLE          hFindFile =FindFirstFile(m_strPath.c_str(),&FindData);
	BOOL            bSuccess  =(hFindFile != INVALID_HANDLE_VALUE);

	if(hFindFile != NULL)	// Make sure we close the search
	    FindClose(hFindFile);

	return bSuccess;
}

//-------------------------------------------------------------
// Post    : Return file size, -1 on error
// Task    : Get file size (in bytes)
//-------------------------------------------------------------
ULONG CPath::GetSize() const
{
    WIN32_FIND_DATA FindData;

	memset(&FindData, 0, sizeof(FindData));

	HANDLE          hFindFile =FindFirstFile(m_strPath.c_str(),&FindData);
	BOOL            bSuccess  =(hFindFile != INVALID_HANDLE_VALUE);

	if(hFindFile != NULL)	// Make sure we close the search
	    FindClose(hFindFile);

    return bSuccess ? FindData.nFileSizeLow : (ULONG)-1;
}

//-------------------------------------------------------------
// Post    : Return file attributes
// Task    : Get attributes of the file
//-------------------------------------------------------------
ULONG CPath::GetAttributes() const
{
    return GetFileAttributes(m_strPath.c_str());
}

//---------------------------------------------------------------------------
// Post    : Return TRUE on success
// Task    : Set the attributes of the file
//---------------------------------------------------------------------------
BOOL CPath::SetAttributes(ULONG dwAttributes)
{
    return SetFileAttributes(m_strPath.c_str(),dwAttributes);
}

//-------------------------------------------------------------
// Post    : Return TRUE on success
// Task    : Get file creation, last acces and/or last modification 
//           time as local time
//-------------------------------------------------------------
BOOL CPath::GetTime(FILETIME *lpCreated, FILETIME *lpAccessed, FILETIME *lpModified) const
{
    WIN32_FIND_DATA findFileData;
	HANDLE hFind =FindFirstFile(m_strPath.c_str(),&findFileData);
	if(hFind == INVALID_HANDLE_VALUE)
        // Oops, no such file system object
		return FALSE;
    FindClose(hFind);

    FILETIME ftLastWriteTimeLocal;
    FileTimeToLocalFileTime(&findFileData.ftLastWriteTime,&ftLastWriteTimeLocal);

	if(lpCreated)
    {
        FILETIME ftCreationTimeLocal;
        FileTimeToLocalFileTime(&findFileData.ftCreationTime,&ftCreationTimeLocal);

        *lpCreated =ftCreationTimeLocal;
        if(!ftCreationTimeLocal.dwLowDateTime &&
           !ftCreationTimeLocal.dwHighDateTime)
           // Adjust time
           *lpCreated =ftLastWriteTimeLocal;
    }

    if(lpAccessed)
    {
        FILETIME ftLastAccessTimeLocal;
        FileTimeToLocalFileTime(&findFileData.ftLastAccessTime,&ftLastAccessTimeLocal);

        *lpAccessed =ftLastAccessTimeLocal;
        if(!ftLastAccessTimeLocal.dwLowDateTime &&
           !ftLastAccessTimeLocal.dwHighDateTime)
           // Adjust time
           *lpAccessed =ftLastWriteTimeLocal;
    }

    if(lpModified)
        *lpModified =ftLastWriteTimeLocal;

    return TRUE;
}

//---------------------------------------------------------------------------
// Post    : Return creation time
// Task    : Get the time this file/folder was created
//---------------------------------------------------------------------------
FILETIME CPath::GetTimeCreated() const
{
    FILETIME file_time;
    ZeroMemory(&file_time,sizeof(file_time));

    GetTime(&file_time,NULL,NULL);

    return file_time;
}

//---------------------------------------------------------------------------
// Post    : Return last access time
// Task    : Get the time this file/folder was last accessed
//---------------------------------------------------------------------------
FILETIME CPath::GetTimeLastAccessed() const
{
    FILETIME file_time;
    ZeroMemory(&file_time,sizeof(file_time));

    GetTime(NULL,&file_time,NULL);

    return file_time;
}

//---------------------------------------------------------------------------
// Post    : Return last modification time
// Task    : Get the time this file/folder was last changed
//---------------------------------------------------------------------------
FILETIME CPath::GetTimeLastModified() const
{
    FILETIME file_time;
    ZeroMemory(&file_time,sizeof(file_time));

    GetTime(NULL,NULL,&file_time);

    return file_time;
}

//---------------------------------------------------------------------------
// Pre     : All time parameters are supposed 2 be local times
// Post    : Return TRUE on success
// Task    : Set the creation, last acces and/or last modification time
//---------------------------------------------------------------------------
BOOL CPath::SetTime(const FILETIME *lpCreated, const FILETIME *lpAccessed, const FILETIME *lpModified)
{
    if(!lpCreated && !lpAccessed && !lpModified)
        // No time params specified
        return FALSE;

    WIN32_FIND_DATA findFileData;
	HANDLE hFind =FindFirstFile(m_strPath.c_str(),&findFileData);
	if(hFind == INVALID_HANDLE_VALUE)
        // Oops, no such file system object
		return FALSE;
    FindClose(hFind);

	if(lpCreated)
        LocalFileTimeToFileTime(lpCreated,&findFileData.ftCreationTime);
    if(lpAccessed)
        LocalFileTimeToFileTime(lpAccessed,&findFileData.ftLastAccessTime);
    if(lpModified)
        LocalFileTimeToFileTime(lpModified,&findFileData.ftLastWriteTime);

    HANDLE hFile =CreateFile(m_strPath.c_str(),GENERIC_WRITE,0,NULL,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,NULL);
    if(hFile != INVALID_HANDLE_VALUE)
    {
        SetFileTime(hFile,lpCreated  ? &findFileData.ftCreationTime   : NULL,
                          lpAccessed ? &findFileData.ftLastAccessTime : NULL,
                          lpModified ? &findFileData.ftLastWriteTime  : NULL);
        CloseHandle(hFile);
        return TRUE;
    }

    return FALSE;
}

//---------------------------------------------------------------------------
// Pre     : lpCreated is supposed 2 be local time
// Post    : Return TRUE on success
// Task    : Set the file's creation time
//---------------------------------------------------------------------------
BOOL CPath::SetTimeCreated(const FILETIME *lpCreated)
{
    return SetTime(lpCreated,NULL,NULL);
}

//---------------------------------------------------------------------------
// Pre     : lpModified is supposed 2 be local time
// Post    : Return TRUE on success
// Task    : Set the file's creation time
//---------------------------------------------------------------------------
BOOL CPath::SetTimeLastModified(const FILETIME *lpModified)
{
    return SetTime(NULL,NULL,lpModified);
}

//---------------------------------------------------------------------------
// Pre     : lpAccessed is supposed 2 be local time
// Post    : Return TRUE on success
// Task    : Set the file's creation time
//---------------------------------------------------------------------------
BOOL CPath::SetTimeLastAccessed(const FILETIME *lpAccessed)
{
    return SetTime(NULL,lpAccessed,NULL);
}

//-------------------------------------------------------------
// Post    : Return TRUE on success
// Task    : Delete file
//-------------------------------------------------------------
BOOL CPath::Delete(BOOL bEvenIfReadOnly) const
{
    ULONG dwAttr =::GetFileAttributes(m_strPath.c_str());
    if(dwAttr == (ULONG)-1)
        // File does not exists
        return FALSE;

    if(((dwAttr & FILE_ATTRIBUTE_READONLY) == FILE_ATTRIBUTE_READONLY) && !bEvenIfReadOnly)
        // File is read-only, and we're not allowed 2 delete it
        return FALSE;

    SetFileAttributes(m_strPath.c_str(),FILE_ATTRIBUTE_NORMAL);
    return DeleteFile(m_strPath.c_str());
}	

//-------------------------------------------------------------
// Post    : Return TRUE on success
// Task    : Rename file
//-------------------------------------------------------------
BOOL CPath::Rename(LPCTSTR lpszNewPath)
{
    return MoveTo(lpszNewPath,FALSE);
}	

//-------------------------------------------------------------
// Post    : Return TRUE on success, FALSE if there is such a target file
//           and we weren't granted permission 2 overwrite file or some error
// Task    : Copy file
//           Since ::CopyFile will not overwrite read only files
//           we will make sure the target file is writable first
//-------------------------------------------------------------
BOOL CPath::CopyTo(LPCTSTR lpcszTargetFile, BOOL bOverwrite)
{
    // Check if the target file exists
    CPath TargetFile(lpcszTargetFile);
    if(TargetFile.Exists())
    {
        // Yeah there is already such a target file 
        // Decide if we should overwrite
        if(!bOverwrite)
            return FALSE;

        // Delete any previous target
        if(!TargetFile.Delete(TRUE))
            return FALSE;
    }

    // CopyFile will set the target's attributes 2 the same as 
    // the source after copying
    return CopyFile(m_strPath.c_str(),lpcszTargetFile,!bOverwrite);
}

//-------------------------------------------------------------
// Post    : Return TRUE on success, FALSE if there is such a target file
//           and we weren't granted permission 2 overwrite file or some error
// Task    : Move file
//-------------------------------------------------------------
BOOL CPath::MoveTo(LPCTSTR lpcszTargetFile, BOOL bOverwrite)
{
    // Check if the target file exists
    CPath TargetFile(lpcszTargetFile);
    if(TargetFile.Exists())
    {
        // Yeah there is already such a target file 
        // Decide if we should overwrite
        if(!bOverwrite)
            return FALSE;

        // Delete any previous target
        if(!TargetFile.Delete(TRUE))
            return FALSE;
    }

   return MoveFile(m_strPath.c_str(),lpcszTargetFile);
}

//-------------------------------------------------------------
// Post    : Return TRUE if attributes do match
// Task    : Compare finder attributes
//-------------------------------------------------------------
BOOL CPath::AttributesMatch(ULONG dwTargetAttributes, ULONG dwFileAttributes)
{
	if (dwTargetAttributes == _A_ALLFILES)
	{
		return true;
	}
	if(dwTargetAttributes == _A_NORMAL)
	{
		return ((_A_SUBDIR & dwFileAttributes) == 0);
	}
	else
	{
		return ( ((dwTargetAttributes & dwFileAttributes) != 0) &&
				 ((_A_SUBDIR & dwTargetAttributes) == (_A_SUBDIR & dwFileAttributes)) );
	}

    return FALSE;
}

//-------------------------------------------------------------
// Post    : Return TRUE if any match found
// Task    : Find the first file that meets this path and the specified attributes
//           You can specify the current attributes of the file or directory 
//           The attributes are represented by a combination (|) of the following 
//           constants:
//
//           _A_ARCH    Archive. Set whenever the file is 
//                      changed, and cleared by the BACKUP command
//           _A_HIDDEN  Hidden file. Not normally seen with 
//                      the DIR command, unless the /AH option 
//                      is used. Returns information about normal 
//                      files as well as files with this attribute
//           _A_NORMAL  Normal. File can be read or written to 
//                      without restriction
//           _A_RDONLY  Read-only. File cannot be opened for writing, 
//                      and a file with the same name cannot be created
//           _A_SUBDIR  Subdirectory
//           _A_SYSTEM  System file. Not normally seen with the DIR 
//                      command, unless the /AS option is used
//
//           These attributes do not follow a simple additive logic
//           Note that _A_NORMAL is 0x00, so it effectively cannot be
//           removed from the attribute set. You will therefore always
//           get normal files, and may also get Archive, Hidden, etc.
//           if you specify those attributes
//           See aso: FindFirstFile, FindNextFile
//-------------------------------------------------------------
BOOL CPath::FindFirst(ULONG dwAttributes /*= _A_NORMAL*/)
{
	m_dwFindFileAttributes =dwAttributes;
	BOOL bGotFile;
	BOOL bWantSubdirectory =(BOOL)(_A_SUBDIR & dwAttributes);

    // Close handle to any previous enumeration
    Exit();

	// i.) Finding first candidate file
	WIN32_FIND_DATA	FindData;
	m_hFindFile =FindFirstFile(m_strPath.c_str(),&FindData);
	bGotFile =(m_hFindFile != INVALID_HANDLE_VALUE);

	while(bGotFile)
	{
		// ii.) Compare candidate to attributes, and filter out the "." and ".." folders
		if(!AttributesMatch(m_dwFindFileAttributes,FindData.dwFileAttributes))
			goto LABEL_GetAnother;
		if(bWantSubdirectory && (FindData.cFileName[0] == '.'))
			goto LABEL_GetAnother;

		// iii.) Found match, prepare result
        if((_A_SUBDIR & FindData.dwFileAttributes) != 0)
            StripTrailingBackslash(m_strPath);

		SetNameExtension(FindData.cFileName);

        if((_A_SUBDIR & FindData.dwFileAttributes) != 0)
            EnsureTrailingBackslash(m_strPath);
		return TRUE;
	
		// iv.) Not found match, get another
	    LABEL_GetAnother:
		bGotFile =FindNextFile(m_hFindFile,&FindData);
	}
	
	return FALSE;
}

//-------------------------------------------------------------
// Post    : Return TRUE if a new match found
// Task    : Find the next file that meets the conditions specified 
//           in the last FindFirst call
//-------------------------------------------------------------
BOOL CPath::FindNext()
{
    if (m_hFindFile == NULL)
		return FALSE;

	WIN32_FIND_DATA	FindData;	
	while(FindNextFile(m_hFindFile,&FindData) != FALSE)
    { // while(FindNext(...))

		if(AttributesMatch(m_dwFindFileAttributes,FindData.dwFileAttributes))
		{ // if(AttributesMatch(...)
	        if((_A_SUBDIR & FindData.dwFileAttributes) == _A_SUBDIR)
			{
				if (IsDirectory())
				{
	                // Found a directory
					UpDirectory();
				} else {
					SetNameExtension("");
				}
				AppendDirectory(FindData.cFileName);
			} else {
                // Found a file
				if (IsDirectory())
				{
	                // Found a directory
					UpDirectory();
				}
				SetNameExtension(FindData.cFileName);
			}
	        if((_A_SUBDIR & FindData.dwFileAttributes) == _A_SUBDIR)
    	    	EnsureTrailingBackslash(m_strPath);
			return TRUE;
		}
	}

	return FALSE;
}

//-------------------------------------------------------------
// Post    : Return TRUE on success
// Task    : Change current working directory of application 2 path
//-------------------------------------------------------------
BOOL CPath::ChangeDirectory()
{
	stdstr DriveDirectory;
	GetDriveDirectory(DriveDirectory);

    return SetCurrentDirectory(DriveDirectory.c_str());
}

//-------------------------------------------------------------
// Post    : Return TRUE if deleted OK
// Task    : Delete everything in the directory
//-------------------------------------------------------------
BOOL CPath::RemoveDirectoryContent()
{
    // Deleting the directory's content
    // Iterate the content of the directory and delete it
    stdstr filename;
    CPath  iterator(*this);
    BOOL   deleted_OK =TRUE;

    // Deleting all contained files
    iterator.SetNameExtension(WILD_NAME_EXTENSION);
    BOOL iterating =iterator.FindFirst(_A_NORMAL | _A_ARCH | _A_HIDDEN | _A_SYSTEM | _A_RDONLY);
    while(iterating)
    {
        // Found something
        deleted_OK =iterator.Delete(TRUE);

        if(!deleted_OK)
            break;

        iterator.SetNameExtension(WILD_NAME_EXTENSION);
        iterating =iterator.FindFirst(_A_NORMAL | _A_ARCH | _A_HIDDEN | _A_SYSTEM | _A_RDONLY);
    }

    if(!deleted_OK)
        return FALSE;

    // Deleting all contained directories
    iterator.SetNameExtension(WILD_NAME_EXTENSION);
    iterating =iterator.FindFirst(_A_HIDDEN | _A_SUBDIR);
    while(iterating)
    {
        // Found something
        deleted_OK =iterator.RemoveDirectory(TRUE);

        if(!deleted_OK)
            break;

        iterator.SetNameExtension(WILD_NAME_EXTENSION);
        iterator.UpDirectory();
        iterating =iterator.FindFirst(_A_HIDDEN | _A_SUBDIR);
    }

    return deleted_OK;
}

//-------------------------------------------------------------
// Post    : Return TRUE if deleted OK
// Task    : Remove the directory
//-------------------------------------------------------------
BOOL CPath::RemoveDirectory(BOOL bEvenIfNotEmpty)
{
    if(bEvenIfNotEmpty)
    {
        // Delete the directory's content
        if(!RemoveDirectoryContent())
            return FALSE;
    }

    // Make sure there is no enumeration in progress,
    // otherwise we we'll get an error (sharing violation) because
    // that search keeps an open handle for this directory
    Exit();
    
    // Deleting this directory (and only if it's empty)
	stdstr DriveDirectory;
	GetDriveDirectory(DriveDirectory);

    return ::RemoveDirectory(DriveDirectory.c_str());
}

//-------------------------------------------------------------
// Pre     : If bCreateIntermediates is TRUE, create all eventually
//           missing parent directories too
// Post    : Return TRUE on success
// Task    : Create new directory
//-------------------------------------------------------------
BOOL CPath::CreateDirectory(BOOL bCreateIntermediates /*= TRUE*/)
{
	stdstr	PathText;
	BOOL	bSuccess;
		
	GetDriveDirectory(PathText);
    StripTrailingBackslash(PathText);
    bSuccess =::CreateDirectory(PathText.c_str(),NULL);
	if(!bSuccess)
	{
		CPath CurrentDir(CPath::CURRENT_DIRECTORY);
		bSuccess = ChangeDirectory();
		CurrentDir.ChangeDirectory();
	}

	if(!bSuccess && bCreateIntermediates)
	{
        stdstr::size_type nDelimiter =PathText.rfind(DIRECTORY_DELIMITER);
        if(nDelimiter == stdstr::npos)
			return FALSE;

		PathText.resize(nDelimiter + 1);
		CPath SubPath(PathText);
		
		if(SubPath.CreateDirectory())
			return CreateDirectory(FALSE);
		else 
			return FALSE;
	}

	return bSuccess;
}			

//Helpers

//------------------------------------------------------------------------
// Task    : Remove first character (if any) if it's chLeading
//------------------------------------------------------------------------
void CPath::cleanPathString(stdstr& rDirectory) const
{
	LPCSTR const DIR_DOUBLEDELIM    = "\\\\";

	std::string::size_type pos = rDirectory.find( DIRECTORY_DELIMITER2 );
	while ( pos != std::string::npos )
	{
		rDirectory.replace( pos, 1, &DIRECTORY_DELIMITER );
		pos = rDirectory.find( DIRECTORY_DELIMITER2, pos + 1 );
	}

	bool AppendEnd = !_strnicmp(rDirectory.c_str(), "\\\\", 2);
	pos = rDirectory.find( DIR_DOUBLEDELIM );
	while ( pos != std::string::npos )
	{
		rDirectory.replace( pos, 1, &DIRECTORY_DELIMITER );
		pos = rDirectory.find( DIR_DOUBLEDELIM, pos + 1 );
	}
	if (AppendEnd)
	{
		rDirectory.insert(0, "\\");
	}
}

void CPath::StripLeadingChar(stdstr& rText, TCHAR chLeading) const
{
    stdstr::size_type nLength =rText.length();
	if(nLength == 0)
		return;

	if(rText[0] == chLeading)
		rText =rText.substr(1);
}


//------------------------------------------------------------------------
// Task    : Remove first character if \
//------------------------------------------------------------------------
void CPath::StripLeadingBackslash(stdstr& Directory) const
{
	stdstr::size_type nLength =Directory.length();

    // If Directory is of the form '\', don't do it
	if(nLength <= 1)
		return;

	if(Directory[0] == DIRECTORY_DELIMITER)
		Directory =Directory.substr(1);
}

//------------------------------------------------------------------------
// Task    : Remove last character (if any) if it's chTrailing
//------------------------------------------------------------------------
void CPath::StripTrailingChar(stdstr& rText, TCHAR chTrailing) const
{
	stdstr::size_type nLength =rText.length();
	if(nLength == 0)
		return;
	
	if(rText[nLength-1] == chTrailing)
		rText.resize(nLength-1);
}

//------------------------------------------------------------------------
// Task    : Remove last character if \
//------------------------------------------------------------------------
void CPath::StripTrailingBackslash(stdstr& rDirectory) const
{
	for (;;)
	{
		stdstr::size_type nLength = rDirectory.length();
		if(nLength <= 1)
		{
			return;
		}

		if(rDirectory[nLength-1] == DIRECTORY_DELIMITER || rDirectory[nLength-1] == DIRECTORY_DELIMITER2)
		{
			rDirectory.resize(nLength-1);
			continue;
		}
		return;
	}
}

//------------------------------------------------------------------------
// Task    : Add a backslash to the end of Directory if there is
//           not already one there
//------------------------------------------------------------------------
void CPath::EnsureTrailingBackslash(stdstr& Directory) const
{
	stdstr::size_type nLength =Directory.length();

	if(Directory.empty() || (Directory[nLength-1] != DIRECTORY_DELIMITER))
		Directory +=DIRECTORY_DELIMITER;
}

//------------------------------------------------------------------------
// Task    : Add a backslash to the beginning of Directory if there
//           is not already one there
//------------------------------------------------------------------------
void CPath::EnsureLeadingBackslash(stdstr& Directory) const
{
	if(Directory.empty() || (Directory[0] != DIRECTORY_DELIMITER))
    {
        stdstr temp =Directory;
		Directory.Format(_T("%c%s"),DIRECTORY_DELIMITER,temp.c_str());
    }
}

//------------------------------------------------------------------------
// Task    : Return path with swapped polarity on all backslashes
//------------------------------------------------------------------------
void CPath::GetAsInternetPath(stdstr& Directory) const
{
    Directory = m_strPath;
	if(!Directory.empty())
    {
        stdstr temp = Directory;
		for (int idx = 0; idx < (int)temp.size(); idx++)
		{
			if (temp[idx]==_T('\\'))
				temp[idx] = _T('/');
		}
		Directory = temp;
    }
	else
		Directory = _T("/");
}