1674 lines
45 KiB
C++
1674 lines
45 KiB
C++
#include "path.h"
|
|
#include "StdString.h"
|
|
#include "Trace.h"
|
|
#include "TraceModulesCommon.h"
|
|
|
|
#ifdef _WIN32
|
|
#pragma warning(push)
|
|
#pragma warning(disable : 4091) // warning C4091: 'typedef ': ignored on left of 'tagGPFIDL_FLAGS' when no variable is declared
|
|
#pragma warning(disable : 4996) // warning C4091: 'typedef ': ignored on left of 'tagGPFIDL_FLAGS' when no variable is declared
|
|
#include <Shlobj.h>
|
|
#include <dos.h>
|
|
|
|
#include <CommDlg.h>
|
|
#pragma warning(pop)
|
|
#else
|
|
#include <dirent.h>
|
|
#include <errno.h>
|
|
#include <sys/stat.h>
|
|
#include <unistd.h>
|
|
#endif
|
|
#include "Platform.h"
|
|
|
|
// g_ModuleLogLevel may be nullptr while AppInit() is still in session in path.cpp.
|
|
// The added check to compare to nullptr here is at least a temporary workaround.
|
|
|
|
#undef WriteTrace
|
|
#ifdef _WIN32
|
|
#define WriteTrace(m, s, format, ...) \
|
|
if (g_ModuleLogLevel != nullptr && g_ModuleLogLevel[(m)] >= (s)) \
|
|
{ \
|
|
WriteTraceFull((m), (s), __FILE__, __LINE__, __FUNCTION__, (format), ##__VA_ARGS__); \
|
|
}
|
|
#else
|
|
#define WriteTrace(m, s, format, ...) \
|
|
if (g_ModuleLogLevel != nullptr && g_ModuleLogLevel[(m)] >= (s)) \
|
|
{ \
|
|
WriteTraceFull((m), (s), __FILE__, __LINE__, __PRETTY_FUNCTION__, (format), ##__VA_ARGS__); \
|
|
}
|
|
#endif
|
|
|
|
// Constants
|
|
|
|
#ifdef _WIN32
|
|
const char DRIVE_DELIMITER = ':';
|
|
const char * const DIR_DOUBLEDELIM = "\\\\";
|
|
const char DIRECTORY_DELIMITER = '\\';
|
|
const char DIRECTORY_DELIMITER2 = '/';
|
|
#else
|
|
const char * const DIR_DOUBLEDELIM = "//";
|
|
const char DIRECTORY_DELIMITER = '/';
|
|
const char DIRECTORY_DELIMITER2 = '\\';
|
|
#endif
|
|
const char EXTENSION_DELIMITER = '.';
|
|
#ifdef _WIN32
|
|
void * CPath::m_hInst = nullptr;
|
|
#endif
|
|
|
|
// Helpers
|
|
|
|
#ifdef _WIN32
|
|
void CPath::SethInst(void * hInst)
|
|
{
|
|
m_hInst = hInst;
|
|
}
|
|
|
|
void * CPath::GethInst()
|
|
{
|
|
return m_hInst;
|
|
}
|
|
#endif
|
|
|
|
// Initialization
|
|
|
|
// Task: Helper function for the various CPath constructors.
|
|
// Initializes the data members and establishes various class invariants.
|
|
|
|
inline void CPath::Init()
|
|
{
|
|
m_dwFindFileAttributes = 0;
|
|
#ifdef _WIN32
|
|
m_hFindFile = nullptr;
|
|
#else
|
|
m_OpenedDir = nullptr;
|
|
m_FindWildcard = "";
|
|
#endif
|
|
}
|
|
|
|
// Task: Helper function for the various CPath destructors. Cleans up various internals.
|
|
|
|
inline void CPath::Exit()
|
|
{
|
|
#ifdef _WIN32
|
|
if (m_hFindFile != nullptr)
|
|
{
|
|
FindClose(m_hFindFile);
|
|
m_hFindFile = nullptr;
|
|
}
|
|
#else
|
|
if (m_OpenedDir != nullptr)
|
|
{
|
|
closedir((DIR *)m_OpenedDir);
|
|
m_OpenedDir = nullptr;
|
|
}
|
|
#endif
|
|
}
|
|
|
|
// 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 to lpszPath
|
|
|
|
CPath::CPath(const char * lpszPath)
|
|
{
|
|
Init();
|
|
m_strPath = lpszPath ? lpszPath : "";
|
|
cleanPathString(m_strPath);
|
|
}
|
|
|
|
CPath::CPath(const char * lpszPath, const char * NameExten)
|
|
{
|
|
WriteTrace(TracePath, TraceDebug, "Start (lpszPath: \"%s\" NameExten: \"%s\")", lpszPath ? lpszPath : "(null)", NameExten ? NameExten : "(null)");
|
|
Init();
|
|
#ifdef _WIN32
|
|
SetDriveDirectory(lpszPath);
|
|
#else
|
|
SetDirectory(lpszPath);
|
|
#endif
|
|
SetNameExtension(NameExten);
|
|
WriteTrace(TracePath, TraceDebug, "Done (m_strPath: \"%s\")", m_strPath.c_str());
|
|
}
|
|
|
|
// Task: Constructs a path and points it to strPath
|
|
|
|
CPath::CPath(const std::string & strPath)
|
|
{
|
|
Init();
|
|
m_strPath = strPath;
|
|
cleanPathString(m_strPath);
|
|
}
|
|
|
|
// Task: Constructs a path and points it to strPath
|
|
|
|
CPath::CPath(const std::string & strPath, const char * NameExten)
|
|
{
|
|
Init();
|
|
#ifdef _WIN32
|
|
SetDriveDirectory(strPath.c_str());
|
|
#else
|
|
SetDirectory(strPath.c_str(), true);
|
|
#endif
|
|
SetNameExtension(NameExten);
|
|
}
|
|
|
|
// Task: Constructs a path and points it to strPath
|
|
|
|
CPath::CPath(const std::string & strPath, const std::string & NameExten)
|
|
{
|
|
Init();
|
|
#ifdef _WIN32
|
|
SetDriveDirectory(strPath.c_str());
|
|
#else
|
|
SetDirectory(strPath.c_str(), true);
|
|
#endif
|
|
SetNameExtension(NameExten.c_str());
|
|
}
|
|
|
|
// 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
|
|
std::string FullyQualified1;
|
|
std::string FullyQualified2;
|
|
|
|
GetFullyQualified(FullyQualified1);
|
|
rPath.GetFullyQualified(FullyQualified2);
|
|
|
|
// Compare them
|
|
return _stricmp(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 to another
|
|
|
|
CPath & CPath::operator=(const CPath & rPath)
|
|
{
|
|
if (this != &rPath)
|
|
{
|
|
m_strPath = rPath.m_strPath;
|
|
}
|
|
return *this;
|
|
}
|
|
|
|
// Post: Return the path, so that assignments can be chained
|
|
// Task: Assign a string to a path
|
|
|
|
CPath & CPath::operator=(const char * lpszPath)
|
|
{
|
|
m_strPath = lpszPath ? lpszPath : "";
|
|
return *this;
|
|
}
|
|
|
|
// Post: Return the path, so that assignments can be chained
|
|
// Task: Assign a string to a path
|
|
|
|
CPath & CPath::operator=(const std::string & strPath)
|
|
{
|
|
m_strPath = strPath;
|
|
return *this;
|
|
}
|
|
|
|
/*
|
|
Post: Converts path to string
|
|
Task: Convert path to string
|
|
Warning: Because this pointer to string point in the data
|
|
of this class, it is possible to cast the result of this
|
|
function in any non-constant pointer and alter the data.
|
|
Very dangerous!
|
|
*/
|
|
|
|
CPath::operator const char *() const
|
|
{
|
|
return (const char *)m_strPath.c_str();
|
|
}
|
|
|
|
CPath::CPath(DIR_CURRENT_DIRECTORY /*sdt*/, const char * NameExten)
|
|
{
|
|
// Application's current directory
|
|
Init();
|
|
CurrentDirectory();
|
|
if (NameExten)
|
|
{
|
|
SetNameExtension(NameExten);
|
|
}
|
|
}
|
|
|
|
#ifdef _WIN32
|
|
CPath::CPath(DIR_MODULE_DIRECTORY /*sdt*/, const char * 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();
|
|
}
|
|
#endif
|
|
|
|
/*
|
|
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 completely 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 nullptr 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.
|
|
*/
|
|
|
|
#ifdef _WIN32
|
|
void CPath::GetComponents(std::string * pDrive, std::string * pDirectory, std::string * pName, std::string * pExtension) const
|
|
{
|
|
WriteTrace(TracePath, TraceDebug, "Start (m_strPath: \"%s\")", m_strPath.c_str());
|
|
|
|
char buff_drive[_MAX_DRIVE + 1] = {0};
|
|
char buff_dir[_MAX_DIR + 1] = {0};
|
|
char buff_name[_MAX_FNAME + 1] = {0};
|
|
char buff_ext[_MAX_EXT + 1] = {0};
|
|
|
|
const char * BasePath = m_strPath.c_str();
|
|
const char * DriveDir = strrchr(BasePath, DRIVE_DELIMITER);
|
|
if (DriveDir != nullptr)
|
|
{
|
|
size_t len = sizeof(buff_dir) < (DriveDir - BasePath) ? sizeof(buff_drive) : DriveDir - BasePath;
|
|
strncpy(buff_drive, BasePath, len);
|
|
BasePath += len + 1;
|
|
}
|
|
|
|
const char * last = strrchr(BasePath, DIRECTORY_DELIMITER);
|
|
if (last != nullptr)
|
|
{
|
|
size_t len = sizeof(buff_dir) < (last - BasePath) ? sizeof(buff_dir) : last - BasePath;
|
|
if (len > 0)
|
|
{
|
|
strncpy(buff_dir, BasePath, len);
|
|
}
|
|
else
|
|
{
|
|
buff_dir[0] = DIRECTORY_DELIMITER;
|
|
buff_dir[1] = '\0';
|
|
}
|
|
strncpy(buff_name, last + 1, sizeof(buff_name));
|
|
}
|
|
else
|
|
{
|
|
strncpy(buff_dir, BasePath, sizeof(buff_dir));
|
|
}
|
|
char * ext = strrchr(buff_name, '.');
|
|
if (ext != nullptr)
|
|
{
|
|
strncpy(buff_ext, ext + 1, sizeof(buff_ext));
|
|
*ext = '\0';
|
|
}
|
|
|
|
if (pDrive)
|
|
{
|
|
*pDrive = buff_drive;
|
|
}
|
|
if (pDirectory)
|
|
{
|
|
*pDirectory = buff_dir;
|
|
}
|
|
if (pName)
|
|
{
|
|
*pName = buff_name;
|
|
}
|
|
if (pExtension)
|
|
{
|
|
*pExtension = buff_ext;
|
|
}
|
|
WriteTrace(TracePath, TraceDebug, "Done (dir: \"%s\" name: \"%s\" ext: \"%s\")", buff_dir, buff_name, buff_ext);
|
|
}
|
|
#else
|
|
void CPath::GetComponents(std::string * pDirectory, std::string * pName, std::string * pExtension) const
|
|
{
|
|
WriteTrace(TracePath, TraceDebug, "Start (m_strPath: \"%s\")", m_strPath.c_str());
|
|
|
|
char buff_dir[260];
|
|
char buff_name[260];
|
|
char buff_ext[260];
|
|
|
|
memset(buff_dir, 0, sizeof(buff_dir));
|
|
memset(buff_name, 0, sizeof(buff_name));
|
|
memset(buff_ext, 0, sizeof(buff_ext));
|
|
|
|
const char * BasePath = m_strPath.c_str();
|
|
const char * last = strrchr(BasePath, DIRECTORY_DELIMITER);
|
|
if (last != nullptr)
|
|
{
|
|
int len = sizeof(buff_dir) < (last - BasePath) ? sizeof(buff_dir) : last - BasePath;
|
|
if (len > 0)
|
|
{
|
|
strncpy(buff_dir, BasePath, len);
|
|
}
|
|
else
|
|
{
|
|
buff_dir[0] = DIRECTORY_DELIMITER;
|
|
buff_dir[1] = '\0';
|
|
}
|
|
strncpy(buff_name, last + 1, sizeof(buff_name));
|
|
}
|
|
else
|
|
{
|
|
strncpy(buff_dir, BasePath, sizeof(buff_dir));
|
|
}
|
|
char * ext = strrchr(buff_name, '.');
|
|
if (ext != nullptr)
|
|
{
|
|
strncpy(buff_ext, ext + 1, sizeof(buff_ext));
|
|
*ext = '\0';
|
|
}
|
|
|
|
if (pDirectory)
|
|
{
|
|
*pDirectory = buff_dir;
|
|
}
|
|
if (pName)
|
|
{
|
|
*pName = buff_name;
|
|
}
|
|
if (pExtension)
|
|
{
|
|
*pExtension = buff_ext;
|
|
}
|
|
WriteTrace(TracePath, TraceDebug, "Done (dir: \"%s\" name: \"%s\" ext: \"%s\")", buff_dir, buff_name, buff_ext);
|
|
}
|
|
#endif
|
|
|
|
// Task: Get drive and directory from path
|
|
|
|
#ifdef _WIN32
|
|
void CPath::GetDriveDirectory(std::string & rDriveDirectory) const
|
|
{
|
|
std::string Drive;
|
|
std::string Directory;
|
|
|
|
GetComponents(&Drive, &Directory);
|
|
rDriveDirectory = Drive;
|
|
if (!Drive.empty())
|
|
{
|
|
rDriveDirectory += DRIVE_DELIMITER;
|
|
}
|
|
rDriveDirectory += Directory;
|
|
}
|
|
|
|
std::string CPath::GetDriveDirectory(void) const
|
|
{
|
|
std::string rDriveDirectory;
|
|
GetDriveDirectory(rDriveDirectory);
|
|
return rDriveDirectory;
|
|
}
|
|
#endif
|
|
|
|
// Task: Get directory from path
|
|
|
|
void CPath::GetDirectory(std::string & rDirectory) const
|
|
{
|
|
#ifdef _WIN32
|
|
GetComponents(nullptr, &rDirectory);
|
|
#else
|
|
GetComponents(&rDirectory);
|
|
#endif
|
|
}
|
|
|
|
std::string CPath::GetDirectory(void) const
|
|
{
|
|
std::string rDirectory;
|
|
GetDirectory(rDirectory);
|
|
return rDirectory;
|
|
}
|
|
|
|
// Task: Get filename and extension from path
|
|
|
|
void CPath::GetNameExtension(std::string & rNameExtension) const
|
|
{
|
|
std::string Name;
|
|
std::string Extension;
|
|
|
|
#ifdef _WIN32
|
|
GetComponents(nullptr, nullptr, &Name, &Extension);
|
|
#else
|
|
GetComponents(nullptr, &Name, &Extension);
|
|
#endif
|
|
rNameExtension = Name;
|
|
if (!Extension.empty())
|
|
{
|
|
rNameExtension += EXTENSION_DELIMITER;
|
|
rNameExtension += Extension;
|
|
}
|
|
}
|
|
|
|
std::string CPath::GetNameExtension(void) const
|
|
{
|
|
std::string rNameExtension;
|
|
GetNameExtension(rNameExtension);
|
|
return rNameExtension;
|
|
}
|
|
|
|
// Task: Get filename from path
|
|
|
|
void CPath::GetName(std::string & rName) const
|
|
{
|
|
#ifdef _WIN32
|
|
GetComponents(nullptr, nullptr, &rName);
|
|
#else
|
|
GetComponents(nullptr, &rName);
|
|
#endif
|
|
}
|
|
|
|
std::string CPath::GetName(void) const
|
|
{
|
|
std::string rName;
|
|
GetName(rName);
|
|
return rName;
|
|
}
|
|
|
|
// Task: Get file extension from path
|
|
|
|
void CPath::GetExtension(std::string & rExtension) const
|
|
{
|
|
#ifdef _WIN32
|
|
GetComponents(nullptr, nullptr, nullptr, &rExtension);
|
|
#else
|
|
GetComponents(nullptr, nullptr, &rExtension);
|
|
#endif
|
|
}
|
|
|
|
std::string CPath::GetExtension(void) const
|
|
{
|
|
std::string rExtension;
|
|
GetExtension(rExtension);
|
|
return rExtension;
|
|
}
|
|
|
|
// Task: Get current directory
|
|
|
|
void CPath::GetLastDirectory(std::string & rDirectory) const
|
|
{
|
|
std::string Directory;
|
|
|
|
rDirectory = "";
|
|
|
|
GetDirectory(Directory);
|
|
StripTrailingBackslash(Directory);
|
|
if (Directory.empty())
|
|
{
|
|
return;
|
|
}
|
|
|
|
std::string::size_type nDelimiter = Directory.rfind(DIRECTORY_DELIMITER);
|
|
rDirectory = Directory.substr(nDelimiter);
|
|
StripLeadingBackslash(rDirectory);
|
|
}
|
|
|
|
std::string CPath::GetLastDirectory(void) const
|
|
{
|
|
std::string rDirecotry;
|
|
GetLastDirectory(rDirecotry);
|
|
return rDirecotry;
|
|
}
|
|
|
|
// Task: Get fully qualified path
|
|
|
|
void CPath::GetFullyQualified(std::string & rFullyQualified) const
|
|
{
|
|
#ifdef _WIN32
|
|
char buff_fullname[MAX_PATH];
|
|
|
|
memset(buff_fullname, 0, sizeof(buff_fullname));
|
|
|
|
_fullpath(buff_fullname, m_strPath.c_str(), MAX_PATH - 1);
|
|
rFullyQualified = buff_fullname;
|
|
#endif
|
|
}
|
|
|
|
// 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
|
|
{
|
|
#ifdef _WIN32
|
|
if (m_strPath.length() > 1 && m_strPath[1] == DRIVE_DELIMITER)
|
|
{
|
|
return false;
|
|
}
|
|
if (m_strPath.length() > 2 && m_strPath[0] == DIRECTORY_DELIMITER && m_strPath[1] == DIRECTORY_DELIMITER)
|
|
{
|
|
return false;
|
|
}
|
|
#else
|
|
if (m_strPath.length() > 1 && m_strPath[0] == DIRECTORY_DELIMITER)
|
|
{
|
|
return false;
|
|
}
|
|
#endif
|
|
return true;
|
|
}
|
|
|
|
// Task: Set path components
|
|
|
|
#ifdef _WIN32
|
|
void CPath::SetComponents(const char * lpszDrive, const char * lpszDirectory, const char * lpszName, const char * lpszExtension)
|
|
{
|
|
char buff_fullname[MAX_PATH];
|
|
|
|
memset(buff_fullname, 0, sizeof(buff_fullname));
|
|
if (lpszDirectory == nullptr || strlen(lpszDirectory) == 0)
|
|
{
|
|
static char empty_dir[] = {DIRECTORY_DELIMITER, '\0'};
|
|
lpszDirectory = empty_dir;
|
|
}
|
|
|
|
_makepath(buff_fullname, lpszDrive, lpszDirectory, lpszName, lpszExtension);
|
|
m_strPath.erase();
|
|
m_strPath = buff_fullname;
|
|
}
|
|
#else
|
|
void CPath::SetComponents(const char * lpszDirectory, const char * lpszName, const char * lpszExtension)
|
|
{
|
|
char buff_fullname[260];
|
|
|
|
memset(buff_fullname, 0, sizeof(buff_fullname));
|
|
if (lpszDirectory != nullptr && lpszDirectory[0] != '\0')
|
|
{
|
|
if (lpszDirectory[0] != DIRECTORY_DELIMITER)
|
|
{
|
|
buff_fullname[0] = DIRECTORY_DELIMITER;
|
|
}
|
|
strncat(buff_fullname, lpszDirectory, sizeof(buff_fullname) - 1);
|
|
std::string::size_type nLength = strlen(buff_fullname);
|
|
if (buff_fullname[nLength - 1] != DIRECTORY_DELIMITER && nLength < sizeof(buff_fullname))
|
|
{
|
|
buff_fullname[nLength] = DIRECTORY_DELIMITER;
|
|
}
|
|
}
|
|
if (lpszName != nullptr)
|
|
{
|
|
strncat(buff_fullname, lpszName, sizeof(buff_fullname) - 1);
|
|
}
|
|
if (lpszExtension != nullptr && lpszExtension[0] != '\0')
|
|
{
|
|
if (lpszExtension[0] != '.')
|
|
{
|
|
strncat(buff_fullname, ".", sizeof(buff_fullname) - 1);
|
|
}
|
|
strncat(buff_fullname, lpszExtension, sizeof(buff_fullname) - 1);
|
|
}
|
|
buff_fullname[sizeof(buff_fullname) - 1] = 0; // Make sure it is null terminated
|
|
m_strPath.erase();
|
|
m_strPath = buff_fullname;
|
|
}
|
|
#endif
|
|
|
|
// Task: Set path's drive
|
|
|
|
#ifdef _WIN32
|
|
void CPath::SetDrive(char chDrive)
|
|
{
|
|
stdstr_f Drive("%c", chDrive);
|
|
std::string Directory;
|
|
std::string Name;
|
|
std::string Extension;
|
|
|
|
GetComponents(nullptr, &Directory, &Name, &Extension);
|
|
SetComponents(Drive.c_str(), Directory.c_str(), Name.c_str(), Extension.c_str());
|
|
}
|
|
#endif
|
|
|
|
// Task: Set path's directory
|
|
|
|
void CPath::SetDirectory(const char * lpszDirectory, bool bEnsureAbsolute /*= false*/)
|
|
{
|
|
WriteTrace(TracePath, TraceDebug, "Start (lpszDirectory: \"%s\" bEnsureAbsolute: %s)", lpszDirectory ? lpszDirectory : "(null)", bEnsureAbsolute ? "true" : "false");
|
|
std::string Directory = lpszDirectory;
|
|
std::string Name;
|
|
std::string Extension;
|
|
|
|
if (bEnsureAbsolute)
|
|
{
|
|
EnsureLeadingBackslash(Directory);
|
|
}
|
|
if (Directory.length() > 0)
|
|
{
|
|
EnsureTrailingBackslash(Directory);
|
|
}
|
|
|
|
#ifdef _WIN32
|
|
std::string Drive;
|
|
GetComponents(&Drive, nullptr, &Name, &Extension);
|
|
SetComponents(Drive.c_str(), Directory.c_str(), Name.c_str(), Extension.c_str());
|
|
#else
|
|
GetComponents(nullptr, &Name, &Extension);
|
|
SetComponents(Directory.c_str(), Name.c_str(), Extension.c_str());
|
|
#endif
|
|
WriteTrace(TracePath, TraceDebug, "Done (m_strPath: \"%s\")", m_strPath.c_str());
|
|
}
|
|
|
|
#ifdef _WIN32
|
|
|
|
// Task: Set path's drive and directory
|
|
|
|
void CPath::SetDriveDirectory(const char * lpszDriveDirectory)
|
|
{
|
|
std::string DriveDirectory = lpszDriveDirectory;
|
|
std::string Name;
|
|
std::string Extension;
|
|
|
|
if (DriveDirectory.length() > 0)
|
|
{
|
|
EnsureTrailingBackslash(DriveDirectory);
|
|
cleanPathString(DriveDirectory);
|
|
}
|
|
|
|
GetComponents(nullptr, nullptr, &Name, &Extension);
|
|
SetComponents(nullptr, DriveDirectory.c_str(), Name.c_str(), Extension.c_str());
|
|
}
|
|
#endif
|
|
|
|
// Task: Set path's filename
|
|
|
|
void CPath::SetName(const char * lpszName)
|
|
{
|
|
std::string Directory;
|
|
std::string Extension;
|
|
|
|
#ifdef _WIN32
|
|
std::string Drive;
|
|
GetComponents(&Drive, &Directory, nullptr, &Extension);
|
|
SetComponents(Drive.c_str(), Directory.c_str(), lpszName, Extension.c_str());
|
|
#else
|
|
GetComponents(&Directory, nullptr, &Extension);
|
|
SetComponents(Directory.c_str(), lpszName, Extension.c_str());
|
|
#endif
|
|
}
|
|
|
|
// Task: Set path's filename
|
|
|
|
void CPath::SetName(int iName)
|
|
{
|
|
std::string Directory;
|
|
std::string Extension;
|
|
char sName[33];
|
|
|
|
memset(sName, 0, sizeof(sName));
|
|
|
|
_snprintf(sName, sizeof(sName), "%d", iName);
|
|
|
|
#ifdef _WIN32
|
|
std::string Drive;
|
|
GetComponents(&Drive, &Directory, nullptr, &Extension);
|
|
SetComponents(Drive.c_str(), Directory.c_str(), sName, Extension.c_str());
|
|
#else
|
|
GetComponents(&Directory, nullptr, &Extension);
|
|
SetComponents(Directory.c_str(), sName, Extension.c_str());
|
|
#endif
|
|
}
|
|
|
|
// Task: Set path's file extension
|
|
|
|
void CPath::SetExtension(const char * lpszExtension)
|
|
{
|
|
std::string Directory;
|
|
std::string Name;
|
|
|
|
#ifdef _WIN32
|
|
std::string Drive;
|
|
GetComponents(&Drive, &Directory, &Name);
|
|
SetComponents(Drive.c_str(), Directory.c_str(), Name.c_str(), lpszExtension);
|
|
#else
|
|
GetComponents(&Directory, &Name);
|
|
SetComponents(Directory.c_str(), Name.c_str(), lpszExtension);
|
|
#endif
|
|
}
|
|
|
|
// Task: Set path's file extension
|
|
|
|
void CPath::SetExtension(int iExtension)
|
|
{
|
|
std::string Directory;
|
|
std::string Name;
|
|
char sExtension[20];
|
|
|
|
memset(sExtension, 0, sizeof(sExtension));
|
|
|
|
_snprintf(sExtension, sizeof(sExtension), "%d", iExtension);
|
|
|
|
#ifdef _WIN32
|
|
std::string Drive;
|
|
GetComponents(&Drive, &Directory, &Name);
|
|
SetComponents(Drive.c_str(), Directory.c_str(), Name.c_str(), sExtension);
|
|
#else
|
|
GetComponents(&Directory, &Name);
|
|
SetComponents(Directory.c_str(), Name.c_str(), sExtension);
|
|
#endif
|
|
}
|
|
|
|
// Task: Set path's filename and extension
|
|
|
|
void CPath::SetNameExtension(const char * lpszNameExtension)
|
|
{
|
|
std::string Directory;
|
|
|
|
#ifdef _WIN32
|
|
std::string Drive;
|
|
GetComponents(&Drive, &Directory);
|
|
SetComponents(Drive.c_str(), Directory.c_str(), lpszNameExtension, nullptr);
|
|
#else
|
|
GetComponents(&Directory);
|
|
SetComponents(Directory.c_str(), lpszNameExtension, nullptr);
|
|
#endif
|
|
}
|
|
|
|
// Task: Append a subdirectory to path's directory
|
|
|
|
void CPath::AppendDirectory(const char * lpszSubDirectory)
|
|
{
|
|
std::string Directory;
|
|
std::string SubDirectory = lpszSubDirectory;
|
|
std::string Name;
|
|
std::string Extension;
|
|
|
|
if (SubDirectory.empty())
|
|
{
|
|
return;
|
|
}
|
|
|
|
// Strip out any preceding backslash
|
|
StripLeadingBackslash(SubDirectory);
|
|
EnsureTrailingBackslash(SubDirectory);
|
|
|
|
#ifdef _WIN32
|
|
std::string Drive;
|
|
GetComponents(&Drive, &Directory, &Name, &Extension);
|
|
#else
|
|
GetComponents(&Directory, &Name, &Extension);
|
|
#endif
|
|
EnsureTrailingBackslash(Directory);
|
|
Directory += SubDirectory;
|
|
|
|
#ifdef _WIN32
|
|
SetComponents(Drive.c_str(), Directory.c_str(), Name.c_str(), Extension.c_str());
|
|
#else
|
|
SetComponents(Directory.c_str(), Name.c_str(), Extension.c_str());
|
|
#endif
|
|
}
|
|
|
|
/*
|
|
Pre: If pLastDirectory is given we will store the name of the
|
|
deepest directory (the one we're exiting) in it
|
|
Task: Remove deepest subdirectory from path
|
|
*/
|
|
|
|
void CPath::UpDirectory(std::string * pLastDirectory /*= nullptr*/)
|
|
{
|
|
std::string Directory;
|
|
|
|
GetDirectory(Directory);
|
|
StripTrailingBackslash(Directory);
|
|
if (Directory.empty())
|
|
return;
|
|
|
|
std::string::size_type nDelimiter = Directory.rfind(DIRECTORY_DELIMITER);
|
|
|
|
if (pLastDirectory != nullptr)
|
|
{
|
|
*pLastDirectory = Directory.substr(nDelimiter);
|
|
StripLeadingBackslash(*pLastDirectory);
|
|
}
|
|
|
|
if (nDelimiter != std::string::npos)
|
|
{
|
|
Directory = Directory.substr(0, nDelimiter);
|
|
}
|
|
|
|
SetDirectory(Directory.c_str());
|
|
}
|
|
|
|
// Task: Set path to current directory
|
|
|
|
void CPath::CurrentDirectory()
|
|
{
|
|
Empty();
|
|
|
|
#ifdef _WIN32
|
|
wchar_t buff_path[260];
|
|
memset(buff_path, 0, sizeof(buff_path));
|
|
::GetCurrentDirectory(sizeof(buff_path), buff_path);
|
|
SetDriveDirectory(stdstr().FromUTF16(buff_path).c_str());
|
|
#else
|
|
char buff_path[260];
|
|
memset(buff_path, 0, sizeof(buff_path));
|
|
getcwd(buff_path, sizeof(buff_path));
|
|
SetDirectory(buff_path);
|
|
#endif
|
|
}
|
|
|
|
// Task: Set path to the name of specified module
|
|
|
|
#ifdef _WIN32
|
|
void CPath::Module(void * hInstance)
|
|
{
|
|
wchar_t buff_path[MAX_PATH];
|
|
memset(buff_path, 0, sizeof(buff_path));
|
|
GetModuleFileName((HINSTANCE)hInstance, buff_path, MAX_PATH);
|
|
m_strPath = stdstr().FromUTF16(buff_path);
|
|
}
|
|
|
|
// Task: Set path to the name of current module
|
|
|
|
void CPath::Module()
|
|
{
|
|
Module(m_hInst);
|
|
}
|
|
|
|
// Task: Set path to the directory of specified module
|
|
|
|
void CPath::ModuleDirectory(void * hInstance)
|
|
{
|
|
Module(hInstance);
|
|
SetNameExtension("");
|
|
}
|
|
|
|
// Task: Set path to the directory of current module
|
|
|
|
void CPath::ModuleDirectory()
|
|
{
|
|
Module();
|
|
SetNameExtension("");
|
|
}
|
|
#endif
|
|
|
|
// Post: Return TRUE if it is a directory
|
|
// Task: Check if this path represents a directory
|
|
|
|
bool CPath::IsDirectory() const
|
|
{
|
|
// Check if this path has a filename
|
|
std::string 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
|
|
{
|
|
WriteTrace(TracePath, TraceDebug, "m_strPath = %s", m_strPath.c_str());
|
|
#ifdef _WIN32
|
|
// Create test path
|
|
CPath TestPath(m_strPath.c_str());
|
|
|
|
std::string DirName;
|
|
TestPath.UpDirectory(&DirName);
|
|
TestPath.SetNameExtension(DirName.c_str());
|
|
|
|
WIN32_FIND_DATA FindData;
|
|
HANDLE hFindFile = FindFirstFile(stdstr((const char *)TestPath).ToUTF16().c_str(), &FindData); // Find anything
|
|
bool res = (hFindFile != INVALID_HANDLE_VALUE);
|
|
|
|
if (hFindFile != nullptr) // Make sure we close the search
|
|
{
|
|
FindClose(hFindFile);
|
|
}
|
|
|
|
#else
|
|
std::string PathText;
|
|
GetDirectory(PathText);
|
|
StripTrailingBackslash(PathText);
|
|
WriteTrace(TracePath, TraceDebug, "Checking if directory \"%s\" exists", PathText.c_str());
|
|
|
|
struct stat fileinfo;
|
|
bool res = stat(PathText.c_str(), &fileinfo) == 0 && S_ISDIR(fileinfo.st_mode);
|
|
#endif
|
|
WriteTrace(TracePath, TraceDebug, "Exist = %s", res ? "True" : "False");
|
|
return res;
|
|
}
|
|
|
|
// Post: Return TRUE if these is such a file
|
|
// Task: Check if file exists
|
|
|
|
bool CPath::Exists() const
|
|
{
|
|
#ifdef _WIN32
|
|
WIN32_FIND_DATA FindData;
|
|
HANDLE hFindFile = FindFirstFile(stdstr(m_strPath).ToUTF16().c_str(), &FindData);
|
|
bool bSuccess = (hFindFile != INVALID_HANDLE_VALUE);
|
|
|
|
if (hFindFile != nullptr) // Make sure we close the search
|
|
{
|
|
FindClose(hFindFile);
|
|
}
|
|
|
|
return bSuccess;
|
|
#else
|
|
struct stat statbuf;
|
|
return stat(m_strPath.c_str(), &statbuf) == 0;
|
|
#endif
|
|
}
|
|
|
|
#ifdef _WIN32
|
|
bool CPath::SelectFile(void * hwndOwner, const char * InitialDir, const char * FileFilter, bool FileMustExist)
|
|
{
|
|
CPath CurrentDir(CURRENT_DIRECTORY);
|
|
std::wstring FileFilterW = stdstr(FileFilter).ToUTF16();
|
|
std::wstring InitialDirW = stdstr(InitialDir).ToUTF16();
|
|
|
|
OPENFILENAME openfilename;
|
|
wchar_t FileName[MAX_PATH];
|
|
memset(&FileName, 0, sizeof(FileName));
|
|
memset(&openfilename, 0, sizeof(openfilename));
|
|
|
|
openfilename.lStructSize = sizeof(openfilename);
|
|
openfilename.hwndOwner = (HWND)hwndOwner;
|
|
openfilename.lpstrFilter = FileFilterW.c_str();
|
|
openfilename.lpstrFile = FileName;
|
|
openfilename.lpstrInitialDir = InitialDirW.c_str();
|
|
openfilename.nMaxFile = MAX_PATH;
|
|
openfilename.Flags = OFN_HIDEREADONLY | (FileMustExist ? OFN_FILEMUSTEXIST : 0);
|
|
|
|
bool res = GetOpenFileName(&openfilename) != 0;
|
|
if (CPath(CURRENT_DIRECTORY) != CurrentDir)
|
|
{
|
|
CurrentDir.ChangeDirectory();
|
|
}
|
|
if (!res)
|
|
{
|
|
return false;
|
|
}
|
|
m_strPath = stdstr().FromUTF16(FileName);
|
|
cleanPathString(m_strPath);
|
|
return true;
|
|
}
|
|
#endif
|
|
|
|
// Post: Return TRUE on success
|
|
// Task: Delete file
|
|
|
|
bool CPath::Delete(bool bEvenIfReadOnly) const
|
|
{
|
|
#ifdef _WIN32
|
|
std::wstring FilePath = stdstr(m_strPath).ToUTF16();
|
|
uint32_t dwAttr = ::GetFileAttributes(FilePath.c_str());
|
|
if (dwAttr == (uint32_t)-1)
|
|
{
|
|
// File does not exist
|
|
return false;
|
|
}
|
|
|
|
if (((dwAttr & FILE_ATTRIBUTE_READONLY) == FILE_ATTRIBUTE_READONLY) && !bEvenIfReadOnly)
|
|
{
|
|
// File is read-only, and we're not allowed to delete it
|
|
return false;
|
|
}
|
|
|
|
SetFileAttributes(FilePath.c_str(), FILE_ATTRIBUTE_NORMAL);
|
|
return DeleteFile(FilePath.c_str()) != 0;
|
|
#else
|
|
return unlink(m_strPath.c_str()) == 0;
|
|
#endif
|
|
}
|
|
|
|
/*
|
|
Post: Return TRUE on success, false if there is such a target file
|
|
and we weren't granted permission to overwrite file or if we get an 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(const char * lpcszTargetFile, bool bOverwrite)
|
|
{
|
|
if (lpcszTargetFile == nullptr)
|
|
{
|
|
return false;
|
|
}
|
|
WriteTrace(TracePath, TraceDebug, "Copy \"%s\" to \"%s\"", m_strPath.c_str(), lpcszTargetFile);
|
|
#ifdef _WIN32
|
|
// 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 to the same as
|
|
// the source after copying
|
|
return CopyFile(stdstr(m_strPath).ToUTF16().c_str(), stdstr(lpcszTargetFile).ToUTF16().c_str(), !bOverwrite) != 0;
|
|
#else
|
|
|
|
bool res = true;
|
|
WriteTrace(TracePath, TraceDebug, "Opening \"%s\" for reading", m_strPath.c_str());
|
|
FILE * infile = fopen(m_strPath.c_str(), "rb");
|
|
if (infile == nullptr)
|
|
{
|
|
WriteTrace(TracePath, TraceWarning, "Failed to open m_strPath = %s", m_strPath.c_str());
|
|
res = false;
|
|
}
|
|
else
|
|
{
|
|
WriteTrace(TracePath, TraceDebug, "Opened \"%s\"", m_strPath.c_str());
|
|
}
|
|
|
|
FILE * outfile = nullptr;
|
|
if (res)
|
|
{
|
|
WriteTrace(TracePath, TraceDebug, "Opening \"%s\" for writing", lpcszTargetFile);
|
|
outfile = fopen(lpcszTargetFile, "wb");
|
|
if (outfile == nullptr)
|
|
{
|
|
WriteTrace(TracePath, TraceWarning, "Failed to open m_strPath = %s errno=%d", lpcszTargetFile, errno);
|
|
res = false;
|
|
}
|
|
else
|
|
{
|
|
WriteTrace(TracePath, TraceDebug, "Opened \"%s\"", lpcszTargetFile);
|
|
}
|
|
}
|
|
|
|
if (res)
|
|
{
|
|
WriteTrace(TracePath, TraceDebug, "Copying data");
|
|
while (!feof(infile))
|
|
{
|
|
char buffer[1024];
|
|
size_t bytes = fread(buffer, 1, sizeof(buffer), infile);
|
|
if (ferror(infile))
|
|
{
|
|
WriteTrace(TracePath, TraceWarning, "Failed to read from %s", m_strPath.c_str());
|
|
res = false;
|
|
break;
|
|
}
|
|
if (!feof(infile))
|
|
{
|
|
size_t written = fwrite(buffer, 1, sizeof(buffer), outfile);
|
|
}
|
|
if (ferror(outfile))
|
|
{
|
|
WriteTrace(TracePath, TraceWarning, "Failed to write to %s, ferror(outfile) = %X", lpcszTargetFile, ferror(outfile));
|
|
res = false;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
struct stat ts;
|
|
if (res)
|
|
{
|
|
if (fstat(fileno(infile), &ts) != 0)
|
|
{
|
|
WriteTrace(TracePath, TraceWarning, "fstat failed on %s, ferror(infile) = %X", m_strPath.c_str(), ferror(infile));
|
|
res = false;
|
|
}
|
|
}
|
|
if (res)
|
|
{
|
|
if (fchmod(fileno(outfile), ts.st_mode) != 0)
|
|
{
|
|
WriteTrace(TracePath, TraceWarning, "fchmod failed on %s, errno = %X", lpcszTargetFile, errno);
|
|
res = false;
|
|
}
|
|
}
|
|
if (infile != nullptr)
|
|
{
|
|
fclose(infile);
|
|
}
|
|
if (outfile != nullptr)
|
|
{
|
|
fclose(outfile);
|
|
}
|
|
WriteTrace(TracePath, TraceDebug, "Done, res: %s", res ? "true" : "false");
|
|
return res;
|
|
#endif
|
|
}
|
|
|
|
// Post: Return TRUE on success, false if there is such a target file
|
|
// and we weren't granted permission to overwrite file or get an error.
|
|
// Task: Move file
|
|
|
|
bool CPath::MoveTo(const char * lpcszTargetFile, bool bOverwrite)
|
|
{
|
|
#ifdef _WIN32
|
|
// 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(stdstr(m_strPath).ToUTF16().c_str(), stdstr(lpcszTargetFile).ToUTF16().c_str()) != 0;
|
|
#else
|
|
return false;
|
|
#endif
|
|
}
|
|
|
|
// Post: Return TRUE if attributes match
|
|
// Task: Compare finder attributes
|
|
|
|
bool CPath::AttributesMatch(uint32_t dwTargetAttributes, uint32_t dwFileAttributes)
|
|
{
|
|
if (dwTargetAttributes == FIND_ATTRIBUTE_ALLFILES)
|
|
{
|
|
return true;
|
|
}
|
|
if (dwTargetAttributes == FIND_ATTRIBUTE_FILES)
|
|
{
|
|
return ((FIND_ATTRIBUTE_SUBDIR & dwFileAttributes) == 0);
|
|
}
|
|
return (((dwTargetAttributes & dwFileAttributes) != 0) && ((FIND_ATTRIBUTE_SUBDIR & dwTargetAttributes) == (FIND_ATTRIBUTE_SUBDIR & dwFileAttributes)));
|
|
}
|
|
|
|
/*
|
|
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:
|
|
FIND_ATTRIBUTE_FILES - 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.
|
|
FIND_ATTRIBUTE_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 FIND_ATTRIBUTE_FILES 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 also: FindFirstFile, FindNextFile
|
|
*/
|
|
|
|
bool CPath::FindFirst(uint32_t dwAttributes /*= FIND_ATTRIBUTE_FILES*/)
|
|
{
|
|
// Close handle to any previous enumeration
|
|
Exit();
|
|
|
|
m_dwFindFileAttributes = dwAttributes;
|
|
#ifdef _WIN32
|
|
BOOL bGotFile;
|
|
BOOL bWantSubdirectory = (BOOL)(FIND_ATTRIBUTE_SUBDIR & dwAttributes);
|
|
|
|
// Finding first candidate file
|
|
WIN32_FIND_DATA FindData;
|
|
m_hFindFile = FindFirstFile(stdstr(m_strPath).ToUTF16().c_str(), &FindData);
|
|
bGotFile = (m_hFindFile != INVALID_HANDLE_VALUE);
|
|
|
|
while (bGotFile)
|
|
{
|
|
// 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;
|
|
|
|
// Found a match, prepare result
|
|
if ((FIND_ATTRIBUTE_SUBDIR & FindData.dwFileAttributes) != 0)
|
|
StripTrailingBackslash(m_strPath);
|
|
|
|
SetNameExtension(stdstr().FromUTF16(FindData.cFileName).c_str());
|
|
|
|
if ((FIND_ATTRIBUTE_SUBDIR & FindData.dwFileAttributes) != 0)
|
|
EnsureTrailingBackslash(m_strPath);
|
|
return TRUE;
|
|
|
|
// Not found a match, get another
|
|
LABEL_GetAnother:
|
|
bGotFile = FindNextFile(m_hFindFile, &FindData);
|
|
}
|
|
#else
|
|
std::string Directory, Name, Extension;
|
|
GetComponents(&Directory, &Name, &Extension);
|
|
|
|
m_FindWildcard = Name;
|
|
if (!Extension.empty())
|
|
{
|
|
m_FindWildcard += EXTENSION_DELIMITER;
|
|
m_FindWildcard += Extension;
|
|
}
|
|
|
|
m_OpenedDir = opendir(Directory.c_str());
|
|
if (m_OpenedDir == nullptr) return false;
|
|
return FindNext();
|
|
#endif
|
|
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()
|
|
{
|
|
#ifdef _WIN32
|
|
if (m_hFindFile == nullptr)
|
|
{
|
|
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(stdstr().FromUTF16(FindData.cFileName).c_str());
|
|
}
|
|
else
|
|
{
|
|
// Found a file
|
|
if (IsDirectory())
|
|
{
|
|
// Found a directory
|
|
UpDirectory();
|
|
}
|
|
SetNameExtension(stdstr().FromUTF16(FindData.cFileName).c_str());
|
|
}
|
|
if ((_A_SUBDIR & FindData.dwFileAttributes) == _A_SUBDIR)
|
|
{
|
|
EnsureTrailingBackslash(m_strPath);
|
|
}
|
|
return TRUE;
|
|
}
|
|
}
|
|
#else
|
|
dirent * pEntry;
|
|
while ((pEntry = readdir((DIR *)m_OpenedDir)))
|
|
{
|
|
uint32_t dwFileAttributes = pEntry->d_type == DT_DIR ? FIND_ATTRIBUTE_SUBDIR : FIND_ATTRIBUTE_FILES;
|
|
|
|
WriteTrace(TracePath, TraceVerbose, "m_dwFindFileAttributes = %X dwFileAttributes = %X AttributesMatch: %s", m_dwFindFileAttributes, dwFileAttributes, AttributesMatch(m_dwFindFileAttributes, dwFileAttributes) ? "true" : "false");
|
|
|
|
// Compare candidate to attributes, and filter out the "." and ".." folders
|
|
if (!AttributesMatch(m_dwFindFileAttributes, dwFileAttributes) ||
|
|
strcmp(pEntry->d_name, ".") == 0 ||
|
|
strcmp(pEntry->d_name, "..") == 0 ||
|
|
!wildcmp(m_FindWildcard.c_str(), pEntry->d_name))
|
|
{
|
|
WriteTrace(TracePath, TraceVerbose, "Continue, d_name = %s", pEntry->d_name);
|
|
continue;
|
|
}
|
|
if ((FIND_ATTRIBUTE_SUBDIR & dwFileAttributes) == FIND_ATTRIBUTE_SUBDIR)
|
|
{
|
|
WriteTrace(TracePath, TraceVerbose, "is a directory");
|
|
if (IsDirectory())
|
|
{
|
|
// Found a directory
|
|
UpDirectory();
|
|
}
|
|
else
|
|
{
|
|
SetNameExtension("");
|
|
}
|
|
AppendDirectory(pEntry->d_name);
|
|
}
|
|
else
|
|
{
|
|
WriteTrace(TracePath, TraceVerbose, "is a file");
|
|
// Found a file
|
|
if (IsDirectory())
|
|
{
|
|
// Found a directory
|
|
UpDirectory();
|
|
}
|
|
SetNameExtension(pEntry->d_name);
|
|
WriteTrace(TracePath, TraceVerbose, "m_strPath: %s pEntry->d_name: %s", m_strPath.c_str(), pEntry->d_name);
|
|
}
|
|
if ((FIND_ATTRIBUTE_SUBDIR & dwFileAttributes) == FIND_ATTRIBUTE_SUBDIR)
|
|
{
|
|
EnsureTrailingBackslash(m_strPath);
|
|
}
|
|
return true;
|
|
}
|
|
#endif
|
|
return false;
|
|
}
|
|
|
|
// Post: Return TRUE on success
|
|
// Task: Change current working directory of application to path
|
|
|
|
bool CPath::ChangeDirectory()
|
|
{
|
|
#ifdef _WIN32
|
|
std::string DriveDirectory;
|
|
GetDriveDirectory(DriveDirectory);
|
|
|
|
return SetCurrentDirectory(stdstr(DriveDirectory).ToUTF16().c_str()) != 0;
|
|
#else
|
|
std::string Dir;
|
|
GetDirectory(Dir);
|
|
|
|
return chdir(Dir.c_str()) == 0;
|
|
#endif
|
|
}
|
|
|
|
CPath & CPath::NormalizePath(CPath BaseDir)
|
|
{
|
|
#ifdef _WIN32
|
|
stdstr Directory = BaseDir.GetDriveDirectory();
|
|
#else
|
|
stdstr Directory = BaseDir.GetDirectory();
|
|
#endif
|
|
bool Changed = false;
|
|
if (IsRelative())
|
|
{
|
|
EnsureTrailingBackslash(Directory);
|
|
Directory += GetDirectory();
|
|
Changed = true;
|
|
}
|
|
strvector Parts = Directory.Tokenize(DIRECTORY_DELIMITER);
|
|
strvector NormalizesParts;
|
|
for (strvector::const_iterator itr = Parts.begin(); itr != Parts.end(); itr++)
|
|
{
|
|
if (*itr == ".")
|
|
{
|
|
Changed = true;
|
|
}
|
|
else if (*itr == "..")
|
|
{
|
|
NormalizesParts.pop_back();
|
|
Changed = true;
|
|
}
|
|
else
|
|
{
|
|
NormalizesParts.push_back(*itr);
|
|
}
|
|
}
|
|
if (Changed)
|
|
{
|
|
Directory.clear();
|
|
for (strvector::const_iterator itr = NormalizesParts.begin(); itr != NormalizesParts.end(); itr++)
|
|
{
|
|
Directory += *itr + DIRECTORY_DELIMITER;
|
|
}
|
|
#ifdef _WIN32
|
|
SetDriveDirectory(Directory.c_str());
|
|
#else
|
|
SetDirectory(Directory.c_str());
|
|
#endif
|
|
}
|
|
return *this;
|
|
}
|
|
|
|
// Pre: If bCreateIntermediates is TRUE, create all eventually missing parent directories too
|
|
// Post: Return TRUE on success
|
|
// Task: Create new directory
|
|
|
|
bool CPath::DirectoryCreate(bool bCreateIntermediates /*= TRUE*/)
|
|
{
|
|
WriteTrace(TracePath, TraceDebug, "m_strPath = %s bCreateIntermediates = %s", m_strPath.c_str(), bCreateIntermediates ? "true" : "false");
|
|
std::string PathText;
|
|
bool bSuccess;
|
|
|
|
if (DirectoryExists())
|
|
{
|
|
WriteTrace(TracePath, TraceDebug, "Directory already exists, res = true");
|
|
return true;
|
|
}
|
|
|
|
#ifdef _WIN32
|
|
GetDriveDirectory(PathText);
|
|
StripTrailingBackslash(PathText);
|
|
WriteTrace(TracePath, TraceDebug, "Create %s", PathText.c_str());
|
|
bSuccess = ::CreateDirectory(stdstr(PathText).ToUTF16().c_str(), nullptr) != 0;
|
|
#else
|
|
GetDirectory(PathText);
|
|
StripTrailingBackslash(PathText);
|
|
WriteTrace(TracePath, TraceDebug, "Create %s", PathText.c_str());
|
|
bSuccess = mkdir(PathText.c_str(), S_IRWXU) == 0;
|
|
if (!bSuccess)
|
|
{
|
|
WriteTrace(TracePath, TraceWarning, "Failed to create \"%s\" errno: %d", PathText.c_str(), errno);
|
|
}
|
|
#endif
|
|
if (!bSuccess && bCreateIntermediates)
|
|
{
|
|
WriteTrace(TracePath, TraceDebug, "Failed creating intermediates");
|
|
std::string::size_type nDelimiter = PathText.rfind(DIRECTORY_DELIMITER);
|
|
if (nDelimiter == std::string::npos)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
PathText.resize(nDelimiter + 1);
|
|
CPath SubPath(PathText);
|
|
|
|
return SubPath.DirectoryCreate() ? DirectoryCreate(false) : false;
|
|
}
|
|
WriteTrace(TracePath, TraceDebug, "res = %s", bSuccess ? "true" : "false");
|
|
return bSuccess;
|
|
}
|
|
|
|
// Helpers
|
|
|
|
// Task: Remove first character (if any) if it's chLeading
|
|
|
|
void CPath::cleanPathString(std::string & rDirectory) const
|
|
{
|
|
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(), DIR_DOUBLEDELIM, 2);
|
|
pos = rDirectory.find(DIR_DOUBLEDELIM);
|
|
while (pos != std::string::npos)
|
|
{
|
|
rDirectory.replace(pos, 2, &DIRECTORY_DELIMITER);
|
|
pos = rDirectory.find(DIR_DOUBLEDELIM, pos + 1);
|
|
}
|
|
if (AppendEnd)
|
|
{
|
|
rDirectory.insert(0, stdstr_f("%c", DIRECTORY_DELIMITER).c_str());
|
|
}
|
|
}
|
|
|
|
void CPath::StripLeadingChar(std::string & rText, char chLeading) const
|
|
{
|
|
std::string::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(std::string & Directory) const
|
|
{
|
|
std::string::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(std::string & rText, char chTrailing) const
|
|
{
|
|
std::string::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(std::string & rDirectory) const
|
|
{
|
|
for (;;)
|
|
{
|
|
std::string::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 the directory if there is not already one there
|
|
|
|
void CPath::EnsureTrailingBackslash(std::string & Directory) const
|
|
{
|
|
std::string::size_type nLength = Directory.length();
|
|
|
|
if (Directory.empty() || (Directory[nLength - 1] != DIRECTORY_DELIMITER))
|
|
{
|
|
Directory += DIRECTORY_DELIMITER;
|
|
}
|
|
}
|
|
|
|
// Task: Add a backslash to the beginning of the directory if there is not already one there
|
|
|
|
void CPath::EnsureLeadingBackslash(std::string & Directory) const
|
|
{
|
|
if (Directory.empty() || (Directory[0] != DIRECTORY_DELIMITER))
|
|
{
|
|
Directory = stdstr_f("%c%s", DIRECTORY_DELIMITER, Directory.c_str());
|
|
}
|
|
}
|
|
|
|
#ifndef _WIN32
|
|
bool CPath::wildcmp(const char * wild, const char * string)
|
|
{
|
|
const char *cp = nullptr, *mp = nullptr;
|
|
|
|
while ((*string) && (*wild != '*'))
|
|
{
|
|
if ((*wild != *string) && (*wild != '?'))
|
|
{
|
|
return 0;
|
|
}
|
|
wild++;
|
|
string++;
|
|
}
|
|
|
|
while (*string)
|
|
{
|
|
if (*wild == '*')
|
|
{
|
|
if (!*++wild)
|
|
{
|
|
return 1;
|
|
}
|
|
mp = wild;
|
|
cp = string + 1;
|
|
}
|
|
else if ((*wild == *string) || (*wild == '?'))
|
|
{
|
|
wild++;
|
|
string++;
|
|
}
|
|
else
|
|
{
|
|
wild = mp;
|
|
string = cp++;
|
|
}
|
|
}
|
|
|
|
while (*wild == '*')
|
|
{
|
|
wild++;
|
|
}
|
|
return !*wild;
|
|
}
|
|
#endif
|