project64/Source/Common/path.cpp

1671 lines
45 KiB
C++
Raw Normal View History

2021-04-12 09:41:28 +00:00
#include "path.h"
2022-10-03 08:04:42 +00:00
#include "StdString.h"
2021-04-12 09:41:28 +00:00
#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>
2022-10-03 08:04:42 +00:00
#include <CommDlg.h>
#pragma warning(pop)
#else
#include <dirent.h>
2016-08-03 11:30:46 +00:00
#include <errno.h>
2022-10-03 08:04:42 +00:00
#include <sys/stat.h>
#include <unistd.h>
#endif
#include "Platform.h"
2021-04-12 11:35:39 +00:00
// 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
2022-10-03 08:04:42 +00:00
#define WriteTrace(m, s, format, ...) \
if (g_ModuleLogLevel != nullptr && g_ModuleLogLevel[(m)] >= (s)) \
{ \
WriteTraceFull((m), (s), __FILE__, __LINE__, __FUNCTION__, (format), ##__VA_ARGS__); \
}
#else
2022-10-03 08:04:42 +00:00
#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
2016-01-12 12:19:50 +00:00
const char DRIVE_DELIMITER = ':';
2016-01-12 06:52:59 +00:00
const char * const DIR_DOUBLEDELIM = "\\\\";
2016-01-12 12:19:50 +00:00
const char DIRECTORY_DELIMITER = '\\';
const char DIRECTORY_DELIMITER2 = '/';
#else
const char * const DIR_DOUBLEDELIM = "//";
const char DIRECTORY_DELIMITER = '/';
const char DIRECTORY_DELIMITER2 = '\\';
#endif
2016-01-12 12:19:50 +00:00
const char EXTENSION_DELIMITER = '.';
#ifdef _WIN32
2021-04-12 11:35:39 +00:00
void * CPath::m_hInst = nullptr;
#endif
// Helpers
#ifdef _WIN32
2016-01-12 06:52:59 +00:00
void CPath::SethInst(void * hInst)
{
2016-01-12 06:52:59 +00:00
m_hInst = hInst;
}
2015-10-25 11:10:54 +00:00
void * CPath::GethInst()
{
2016-01-12 06:52:59 +00:00
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()
{
2016-01-12 06:52:59 +00:00
m_dwFindFileAttributes = 0;
#ifdef _WIN32
2021-04-12 11:35:39 +00:00
m_hFindFile = nullptr;
#else
2021-04-12 11:35:39 +00:00
m_OpenedDir = nullptr;
m_FindWildcard = "";
#endif
}
// Task: Helper function for the various CPath destructors. Cleans up various internals.
inline void CPath::Exit()
{
#ifdef _WIN32
2021-04-12 11:35:39 +00:00
if (m_hFindFile != nullptr)
{
2016-01-12 06:52:59 +00:00
FindClose(m_hFindFile);
2021-04-12 11:35:39 +00:00
m_hFindFile = nullptr;
}
#else
2021-04-12 11:35:39 +00:00
if (m_OpenedDir != nullptr)
{
2022-10-03 08:04:42 +00:00
closedir((DIR *)m_OpenedDir);
2021-04-12 11:35:39 +00:00
m_OpenedDir = nullptr;
}
#endif
}
// Construction/Destruction
// Task: Constructs a path
CPath::CPath()
{
2016-01-12 06:52:59 +00:00
Init();
Empty();
}
// Task: Constructs a path as copy of another
2022-10-03 08:04:42 +00:00
CPath::CPath(const CPath & rPath)
{
2016-01-12 06:52:59 +00:00
Init();
m_strPath = rPath.m_strPath;
}
// Task: Constructs a path and points it to lpszPath
2015-10-25 11:10:54 +00:00
CPath::CPath(const char * lpszPath)
{
2016-01-12 06:52:59 +00:00
Init();
m_strPath = lpszPath ? lpszPath : "";
cleanPathString(m_strPath);
}
2015-10-25 11:10:54 +00:00
CPath::CPath(const char * lpszPath, const char * NameExten)
{
2016-08-03 11:30:46 +00:00
WriteTrace(TracePath, TraceDebug, "Start (lpszPath: \"%s\" NameExten: \"%s\")", lpszPath ? lpszPath : "(null)", NameExten ? NameExten : "(null)");
2016-01-12 06:52:59 +00:00
Init();
#ifdef _WIN32
2016-01-12 06:52:59 +00:00
SetDriveDirectory(lpszPath);
#else
SetDirectory(lpszPath);
#endif
2016-01-12 06:52:59 +00:00
SetNameExtension(NameExten);
2016-08-03 11:30:46 +00:00
WriteTrace(TracePath, TraceDebug, "Done (m_strPath: \"%s\")", m_strPath.c_str());
}
// Task: Constructs a path and points it to strPath
2022-10-03 08:04:42 +00:00
CPath::CPath(const std::string & strPath)
{
Init();
2016-01-12 06:52:59 +00:00
m_strPath = strPath;
cleanPathString(m_strPath);
}
// Task: Constructs a path and points it to strPath
2022-10-03 08:04:42 +00:00
CPath::CPath(const std::string & strPath, const char * NameExten)
{
2016-01-12 06:52:59 +00:00
Init();
#ifdef _WIN32
2016-01-12 06:52:59 +00:00
SetDriveDirectory(strPath.c_str());
#else
2022-10-03 08:04:42 +00:00
SetDirectory(strPath.c_str(), true);
#endif
2016-01-12 06:52:59 +00:00
SetNameExtension(NameExten);
}
// Task: Constructs a path and points it to strPath
2022-10-03 08:04:42 +00:00
CPath::CPath(const std::string & strPath, const std::string & NameExten)
{
2016-01-12 06:52:59 +00:00
Init();
#ifdef _WIN32
2016-01-12 06:52:59 +00:00
SetDriveDirectory(strPath.c_str());
#else
2022-10-03 08:04:42 +00:00
SetDirectory(strPath.c_str(), true);
#endif
2016-01-12 06:52:59 +00:00
SetNameExtension(NameExten.c_str());
}
// Task: Cleanup and destruct a path object
CPath::~CPath()
{
2016-01-12 06:52:59 +00:00
Exit();
}
// Post: Return TRUE if paths are equal
// Task: Check if the two path are the same
2022-10-03 08:04:42 +00:00
bool CPath::operator==(const CPath & rPath) const
{
// Get fully qualified versions of the paths
2016-01-12 06:52:59 +00:00
std::string FullyQualified1;
2015-10-25 11:10:54 +00:00
std::string FullyQualified2;
2016-01-12 06:52:59 +00:00
GetFullyQualified(FullyQualified1);
rPath.GetFullyQualified(FullyQualified2);
// Compare them
2016-01-12 06:52:59 +00:00
return _stricmp(FullyQualified1.c_str(), FullyQualified2.c_str()) == 0;
}
// Post: Return TRUE if paths are different
// Task: Check if the two path are different
2022-10-03 08:04:42 +00:00
bool CPath::operator!=(const CPath & rPath) const
{
return !(*this == rPath);
}
// Task: Assign a path to another
2022-10-03 08:04:42 +00:00
CPath & CPath::operator=(const CPath & rPath)
2016-01-12 06:52:59 +00:00
{
if (this != &rPath)
{
2016-01-12 06:52:59 +00:00
m_strPath = rPath.m_strPath;
}
return *this;
}
// Post: Return the path, so that assignments can be chained
// Task: Assign a string to a path
2022-10-03 08:04:42 +00:00
CPath & CPath::operator=(const char * lpszPath)
{
2016-01-12 06:52:59 +00:00
m_strPath = lpszPath ? lpszPath : "";
return *this;
}
// Post: Return the path, so that assignments can be chained
// Task: Assign a string to a path
2022-10-03 08:04:42 +00:00
CPath & CPath::operator=(const std::string & strPath)
{
2016-01-12 06:52:59 +00:00
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!
*/
2015-10-25 11:10:54 +00:00
CPath::operator const char *() const
{
2015-10-25 11:10:54 +00:00
return (const char *)m_strPath.c_str();
}
2015-10-25 11:10:54 +00:00
CPath::CPath(DIR_CURRENT_DIRECTORY /*sdt*/, const char * NameExten)
{
2016-01-12 06:52:59 +00:00
// Application's current directory
Init();
CurrentDirectory();
2022-10-03 08:04:42 +00:00
if (NameExten)
{
SetNameExtension(NameExten);
}
}
#ifdef _WIN32
2015-10-25 11:10:54 +00:00
CPath::CPath(DIR_MODULE_DIRECTORY /*sdt*/, const char * NameExten)
{
2016-01-12 06:52:59 +00:00
// The directory where the executable of this app is
Init();
ModuleDirectory();
2022-10-03 08:04:42 +00:00
if (NameExten)
{
SetNameExtension(NameExten);
}
}
CPath::CPath(DIR_MODULE_FILE /*sdt*/)
{
2016-01-12 06:52:59 +00:00
// 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.
2021-04-12 11:35:39 +00:00
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
2022-10-03 08:04:42 +00:00
void CPath::GetComponents(std::string * pDrive, std::string * pDirectory, std::string * pName, std::string * pExtension) const
{
2018-11-19 10:46:58 +00:00
WriteTrace(TracePath, TraceDebug, "Start (m_strPath: \"%s\")", m_strPath.c_str());
2022-10-03 08:04:42 +00:00
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};
2016-01-12 06:52:59 +00:00
2018-11-19 10:46:58 +00:00
const char * BasePath = m_strPath.c_str();
const char * DriveDir = strrchr(BasePath, DRIVE_DELIMITER);
2021-04-12 11:35:39 +00:00
if (DriveDir != nullptr)
2016-01-12 12:19:50 +00:00
{
size_t len = sizeof(buff_dir) < (DriveDir - BasePath) ? sizeof(buff_drive) : DriveDir - BasePath;
2018-11-19 10:46:58 +00:00
strncpy(buff_drive, BasePath, len);
BasePath += len + 1;
2016-01-12 12:19:50 +00:00
}
2018-11-19 10:46:58 +00:00
const char * last = strrchr(BasePath, DIRECTORY_DELIMITER);
2021-04-12 11:35:39 +00:00
if (last != nullptr)
2016-01-12 12:19:50 +00:00
{
size_t len = sizeof(buff_dir) < (last - BasePath) ? sizeof(buff_dir) : last - BasePath;
2018-11-19 10:46:58 +00:00
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));
2016-01-12 12:19:50 +00:00
}
2018-11-19 10:46:58 +00:00
else
2016-01-12 12:19:50 +00:00
{
2018-11-19 10:46:58 +00:00
strncpy(buff_dir, BasePath, sizeof(buff_dir));
2016-01-12 12:19:50 +00:00
}
2018-11-19 10:46:58 +00:00
char * ext = strrchr(buff_name, '.');
2021-04-12 11:35:39 +00:00
if (ext != nullptr)
2016-01-12 12:19:50 +00:00
{
2018-11-19 10:46:58 +00:00
strncpy(buff_ext, ext + 1, sizeof(buff_ext));
*ext = '\0';
2016-01-12 12:19:50 +00:00
}
2016-01-12 06:52:59 +00:00
if (pDrive)
2016-01-12 12:19:50 +00:00
{
2018-11-19 10:46:58 +00:00
*pDrive = buff_drive;
2016-01-12 12:19:50 +00:00
}
2016-01-12 06:52:59 +00:00
if (pDirectory)
2016-01-12 12:19:50 +00:00
{
2018-11-19 10:46:58 +00:00
*pDirectory = buff_dir;
}
if (pName)
{
*pName = buff_name;
2016-01-12 12:19:50 +00:00
}
2016-01-12 06:52:59 +00:00
if (pExtension)
2016-01-12 12:19:50 +00:00
{
2018-11-19 10:46:58 +00:00
*pExtension = buff_ext;
2016-01-12 12:19:50 +00:00
}
2018-11-19 10:46:58 +00:00
WriteTrace(TracePath, TraceDebug, "Done (dir: \"%s\" name: \"%s\" ext: \"%s\")", buff_dir, buff_name, buff_ext);
}
#else
2022-10-03 08:04:42 +00:00
void CPath::GetComponents(std::string * pDirectory, std::string * pName, std::string * pExtension) const
{
2022-10-03 08:04:42 +00:00
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();
2022-10-03 08:04:42 +00:00
const char * last = strrchr(BasePath, DIRECTORY_DELIMITER);
2021-04-12 11:35:39 +00:00
if (last != nullptr)
{
int len = sizeof(buff_dir) < (last - BasePath) ? sizeof(buff_dir) : last - BasePath;
if (len > 0)
{
2022-10-03 08:04:42 +00:00
strncpy(buff_dir, BasePath, len);
}
else
{
buff_dir[0] = DIRECTORY_DELIMITER;
buff_dir[1] = '\0';
}
2022-10-03 08:04:42 +00:00
strncpy(buff_name, last + 1, sizeof(buff_name));
}
else
{
2022-10-03 08:04:42 +00:00
strncpy(buff_dir, BasePath, sizeof(buff_dir));
}
2022-10-03 08:04:42 +00:00
char * ext = strrchr(buff_name, '.');
2021-04-12 11:35:39 +00:00
if (ext != nullptr)
{
2022-10-03 08:04:42 +00:00
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;
2016-08-03 11:30:46 +00:00
}
2022-10-03 08:04:42 +00:00
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
2022-10-03 08:04:42 +00:00
void CPath::GetDriveDirectory(std::string & rDriveDirectory) const
{
2015-10-25 11:10:54 +00:00
std::string Drive;
std::string Directory;
2016-01-12 06:52:59 +00:00
GetComponents(&Drive, &Directory);
rDriveDirectory = Drive;
if (!Drive.empty())
{
2016-01-12 06:52:59 +00:00
rDriveDirectory += DRIVE_DELIMITER;
}
2018-11-19 10:46:58 +00:00
rDriveDirectory += Directory;
}
2015-10-25 11:10:54 +00:00
std::string CPath::GetDriveDirectory(void) const
{
2016-01-12 06:52:59 +00:00
std::string rDriveDirectory;
GetDriveDirectory(rDriveDirectory);
return rDriveDirectory;
}
#endif
// Task: Get directory from path
2022-10-03 08:04:42 +00:00
void CPath::GetDirectory(std::string & rDirectory) const
{
#ifdef _WIN32
2021-04-12 11:35:39 +00:00
GetComponents(nullptr, &rDirectory);
#else
GetComponents(&rDirectory);
#endif
2016-01-12 06:52:59 +00:00
}
2015-10-25 11:10:54 +00:00
std::string CPath::GetDirectory(void) const
{
2016-01-12 06:52:59 +00:00
std::string rDirectory;
2016-01-12 12:19:50 +00:00
GetDirectory(rDirectory);
2016-01-12 06:52:59 +00:00
return rDirectory;
}
// Task: Get filename and extension from path
2022-10-03 08:04:42 +00:00
void CPath::GetNameExtension(std::string & rNameExtension) const
{
2015-10-25 11:10:54 +00:00
std::string Name;
std::string Extension;
#ifdef _WIN32
2021-04-12 11:35:39 +00:00
GetComponents(nullptr, nullptr, &Name, &Extension);
#else
2021-04-12 11:35:39 +00:00
GetComponents(nullptr, &Name, &Extension);
#endif
2016-01-12 06:52:59 +00:00
rNameExtension = Name;
if (!Extension.empty())
{
2016-01-12 06:52:59 +00:00
rNameExtension += EXTENSION_DELIMITER;
rNameExtension += Extension;
}
}
2015-10-25 11:10:54 +00:00
std::string CPath::GetNameExtension(void) const
{
2016-01-12 06:52:59 +00:00
std::string rNameExtension;
GetNameExtension(rNameExtension);
return rNameExtension;
}
// Task: Get filename from path
2022-10-03 08:04:42 +00:00
void CPath::GetName(std::string & rName) const
{
#ifdef _WIN32
2021-04-12 11:35:39 +00:00
GetComponents(nullptr, nullptr, &rName);
#else
2021-04-12 11:35:39 +00:00
GetComponents(nullptr, &rName);
#endif
}
2015-10-25 11:10:54 +00:00
std::string CPath::GetName(void) const
{
2016-01-12 06:52:59 +00:00
std::string rName;
2016-01-12 12:19:50 +00:00
GetName(rName);
2016-01-12 06:52:59 +00:00
return rName;
}
// Task: Get file extension from path
2022-10-03 08:04:42 +00:00
void CPath::GetExtension(std::string & rExtension) const
{
#ifdef _WIN32
2021-04-12 11:35:39 +00:00
GetComponents(nullptr, nullptr, nullptr, &rExtension);
#else
2021-04-12 11:35:39 +00:00
GetComponents(nullptr, nullptr, &rExtension);
#endif
2016-01-12 06:52:59 +00:00
}
2015-10-25 11:10:54 +00:00
std::string CPath::GetExtension(void) const
{
2016-01-12 06:52:59 +00:00
std::string rExtension;
2016-01-12 12:19:50 +00:00
GetExtension(rExtension);
2016-01-12 06:52:59 +00:00
return rExtension;
}
// Task: Get current directory
2022-10-03 08:04:42 +00:00
void CPath::GetLastDirectory(std::string & rDirectory) const
{
2016-01-12 06:52:59 +00:00
std::string Directory;
rDirectory = "";
GetDirectory(Directory);
StripTrailingBackslash(Directory);
if (Directory.empty())
{
return;
}
std::string::size_type nDelimiter = Directory.rfind(DIRECTORY_DELIMITER);
2016-01-12 06:52:59 +00:00
rDirectory = Directory.substr(nDelimiter);
StripLeadingBackslash(rDirectory);
}
std::string CPath::GetLastDirectory(void) const
{
2016-01-12 06:52:59 +00:00
std::string rDirecotry;
GetLastDirectory(rDirecotry);
return rDirecotry;
}
// Task: Get fully qualified path
2022-10-03 08:04:42 +00:00
void CPath::GetFullyQualified(std::string & rFullyQualified) const
{
#ifdef _WIN32
2016-01-12 12:19:50 +00:00
char buff_fullname[MAX_PATH];
2016-01-12 06:52:59 +00:00
memset(buff_fullname, 0, sizeof(buff_fullname));
2016-01-12 06:52:59 +00:00
_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:\...)
2015-10-25 11:10:54 +00:00
bool CPath::IsRelative() const
{
#ifdef _WIN32
2016-01-12 06:52:59 +00:00
if (m_strPath.length() > 1 && m_strPath[1] == DRIVE_DELIMITER)
{
return false;
}
2022-10-03 08:04:42 +00:00
if (m_strPath.length() > 2 && m_strPath[0] == DIRECTORY_DELIMITER && m_strPath[1] == DIRECTORY_DELIMITER)
{
return false;
}
#else
2022-10-03 08:04:42 +00:00
if (m_strPath.length() > 1 && m_strPath[0] == DIRECTORY_DELIMITER)
{
return false;
}
#endif
2016-01-12 06:52:59 +00:00
return true;
}
// Task: Set path components
#ifdef _WIN32
2016-01-12 12:19:50 +00:00
void CPath::SetComponents(const char * lpszDrive, const char * lpszDirectory, const char * lpszName, const char * lpszExtension)
{
2016-01-12 12:19:50 +00:00
char buff_fullname[MAX_PATH];
2016-01-12 06:52:59 +00:00
memset(buff_fullname, 0, sizeof(buff_fullname));
2021-04-12 11:35:39 +00:00
if (lpszDirectory == nullptr || strlen(lpszDirectory) == 0)
{
2022-10-03 08:04:42 +00:00
static char empty_dir[] = {DIRECTORY_DELIMITER, '\0'};
lpszDirectory = empty_dir;
}
2016-01-12 06:52:59 +00:00
_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));
2021-04-12 11:35:39 +00:00
if (lpszDirectory != nullptr && lpszDirectory[0] != '\0')
{
2022-10-03 08:04:42 +00:00
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);
2022-10-03 08:04:42 +00:00
if (buff_fullname[nLength - 1] != DIRECTORY_DELIMITER && nLength < sizeof(buff_fullname))
{
buff_fullname[nLength] = DIRECTORY_DELIMITER;
}
}
2021-04-12 11:35:39 +00:00
if (lpszName != nullptr)
{
2022-10-03 08:04:42 +00:00
strncat(buff_fullname, lpszName, sizeof(buff_fullname) - 1);
}
2021-04-12 11:35:39 +00:00
if (lpszExtension != nullptr && lpszExtension[0] != '\0')
{
2016-05-15 10:20:55 +00:00
if (lpszExtension[0] != '.')
{
2022-10-03 08:04:42 +00:00
strncat(buff_fullname, ".", sizeof(buff_fullname) - 1);
2016-05-15 10:20:55 +00:00
}
2022-10-03 08:04:42 +00:00
strncat(buff_fullname, lpszExtension, sizeof(buff_fullname) - 1);
}
buff_fullname[sizeof(buff_fullname) - 1] = 0; // Make sure it is null terminated
m_strPath.erase();
2016-01-12 06:52:59 +00:00
m_strPath = buff_fullname;
}
#endif
// Task: Set path's drive
#ifdef _WIN32
2016-01-12 12:19:50 +00:00
void CPath::SetDrive(char chDrive)
{
2016-01-12 06:52:59 +00:00
stdstr_f Drive("%c", chDrive);
2022-10-03 08:04:42 +00:00
std::string Directory;
std::string Name;
std::string Extension;
2016-01-12 06:52:59 +00:00
2021-04-12 11:35:39 +00:00
GetComponents(nullptr, &Directory, &Name, &Extension);
2016-01-12 06:52:59 +00:00
SetComponents(Drive.c_str(), Directory.c_str(), Name.c_str(), Extension.c_str());
}
#endif
// Task: Set path's directory
2016-01-12 12:19:50 +00:00
void CPath::SetDirectory(const char * lpszDirectory, bool bEnsureAbsolute /*= false*/)
{
WriteTrace(TracePath, TraceDebug, "Start (lpszDirectory: \"%s\" bEnsureAbsolute: %s)", lpszDirectory ? lpszDirectory : "(null)", bEnsureAbsolute ? "true" : "false");
2022-10-03 08:04:42 +00:00
std::string Directory = lpszDirectory;
std::string Name;
std::string Extension;
2016-01-12 06:52:59 +00:00
if (bEnsureAbsolute)
{
EnsureLeadingBackslash(Directory);
}
if (Directory.length() > 0)
{
EnsureTrailingBackslash(Directory);
}
#ifdef _WIN32
2022-10-03 08:04:42 +00:00
std::string Drive;
2021-04-12 11:35:39 +00:00
GetComponents(&Drive, nullptr, &Name, &Extension);
2016-01-12 06:52:59 +00:00
SetComponents(Drive.c_str(), Directory.c_str(), Name.c_str(), Extension.c_str());
#else
2021-04-12 11:35:39 +00:00
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());
2016-01-12 06:52:59 +00:00
}
#ifdef _WIN32
// Task: Set path's drive and directory
2015-10-25 11:10:54 +00:00
void CPath::SetDriveDirectory(const char * lpszDriveDirectory)
{
2022-10-03 08:04:42 +00:00
std::string DriveDirectory = lpszDriveDirectory;
std::string Name;
std::string Extension;
2016-01-12 06:52:59 +00:00
if (DriveDirectory.length() > 0)
{
EnsureTrailingBackslash(DriveDirectory);
cleanPathString(DriveDirectory);
}
2016-01-12 06:52:59 +00:00
2021-04-12 11:35:39 +00:00
GetComponents(nullptr, nullptr, &Name, &Extension);
SetComponents(nullptr, DriveDirectory.c_str(), Name.c_str(), Extension.c_str());
2016-01-12 06:52:59 +00:00
}
#endif
// Task: Set path's filename
2015-10-25 11:10:54 +00:00
void CPath::SetName(const char * lpszName)
{
2022-10-03 08:04:42 +00:00
std::string Directory;
std::string Extension;
2016-01-12 06:52:59 +00:00
#ifdef _WIN32
2022-10-03 08:04:42 +00:00
std::string Drive;
2021-04-12 11:35:39 +00:00
GetComponents(&Drive, &Directory, nullptr, &Extension);
2016-01-12 06:52:59 +00:00
SetComponents(Drive.c_str(), Directory.c_str(), lpszName, Extension.c_str());
#else
2021-04-12 11:35:39 +00:00
GetComponents(&Directory, nullptr, &Extension);
SetComponents(Directory.c_str(), lpszName, Extension.c_str());
#endif
}
// Task: Set path's filename
void CPath::SetName(int iName)
{
2022-10-03 08:04:42 +00:00
std::string Directory;
std::string Extension;
char sName[33];
2016-01-12 06:52:59 +00:00
memset(sName, 0, sizeof(sName));
2016-01-12 12:19:50 +00:00
_snprintf(sName, sizeof(sName), "%d", iName);
#ifdef _WIN32
2022-10-03 08:04:42 +00:00
std::string Drive;
2021-04-12 11:35:39 +00:00
GetComponents(&Drive, &Directory, nullptr, &Extension);
2016-01-12 06:52:59 +00:00
SetComponents(Drive.c_str(), Directory.c_str(), sName, Extension.c_str());
#else
2021-04-12 11:35:39 +00:00
GetComponents(&Directory, nullptr, &Extension);
SetComponents(Directory.c_str(), sName, Extension.c_str());
#endif
2016-01-12 06:52:59 +00:00
}
// Task: Set path's file extension
2015-10-25 11:10:54 +00:00
void CPath::SetExtension(const char * lpszExtension)
{
2022-10-03 08:04:42 +00:00
std::string Directory;
std::string Name;
2016-01-12 06:52:59 +00:00
#ifdef _WIN32
2022-10-03 08:04:42 +00:00
std::string Drive;
2016-01-12 06:52:59 +00:00
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)
{
2022-10-03 08:04:42 +00:00
std::string Directory;
std::string Name;
2016-01-12 12:19:50 +00:00
char sExtension[20];
2016-01-12 06:52:59 +00:00
memset(sExtension, 0, sizeof(sExtension));
2016-01-12 12:19:50 +00:00
_snprintf(sExtension, sizeof(sExtension), "%d", iExtension);
#ifdef _WIN32
2022-10-03 08:04:42 +00:00
std::string Drive;
2016-01-12 06:52:59 +00:00
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
2015-10-25 11:10:54 +00:00
void CPath::SetNameExtension(const char * lpszNameExtension)
{
2022-10-03 08:04:42 +00:00
std::string Directory;
#ifdef _WIN32
2022-10-03 08:04:42 +00:00
std::string Drive;
2016-01-12 06:52:59 +00:00
GetComponents(&Drive, &Directory);
2021-04-12 11:35:39 +00:00
SetComponents(Drive.c_str(), Directory.c_str(), lpszNameExtension, nullptr);
#else
GetComponents(&Directory);
2021-04-12 11:35:39 +00:00
SetComponents(Directory.c_str(), lpszNameExtension, nullptr);
#endif
2016-01-12 06:52:59 +00:00
}
// Task: Append a subdirectory to path's directory
2015-10-25 11:10:54 +00:00
void CPath::AppendDirectory(const char * lpszSubDirectory)
2016-01-12 06:52:59 +00:00
{
2022-10-03 08:04:42 +00:00
std::string Directory;
std::string SubDirectory = lpszSubDirectory;
std::string Name;
std::string Extension;
2016-01-12 06:52:59 +00:00
if (SubDirectory.empty())
2016-01-12 12:19:50 +00:00
{
2016-01-12 06:52:59 +00:00
return;
2016-01-12 12:19:50 +00:00
}
// Strip out any preceding backslash
2016-01-12 06:52:59 +00:00
StripLeadingBackslash(SubDirectory);
EnsureTrailingBackslash(SubDirectory);
#ifdef _WIN32
2022-10-03 08:04:42 +00:00
std::string Drive;
2016-01-12 06:52:59 +00:00
GetComponents(&Drive, &Directory, &Name, &Extension);
#else
GetComponents(&Directory, &Name, &Extension);
#endif
2016-01-12 06:52:59 +00:00
EnsureTrailingBackslash(Directory);
Directory += SubDirectory;
#ifdef _WIN32
2016-01-12 06:52:59 +00:00
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
*/
2022-10-03 08:04:42 +00:00
void CPath::UpDirectory(std::string * pLastDirectory /*= nullptr*/)
{
2016-01-12 06:52:59 +00:00
std::string Directory;
GetDirectory(Directory);
StripTrailingBackslash(Directory);
if (Directory.empty())
return;
std::string::size_type nDelimiter = Directory.rfind(DIRECTORY_DELIMITER);
2021-04-12 11:35:39 +00:00
if (pLastDirectory != nullptr)
2016-01-12 06:52:59 +00:00
{
*pLastDirectory = Directory.substr(nDelimiter);
StripLeadingBackslash(*pLastDirectory);
}
if (nDelimiter != std::string::npos)
2016-01-12 12:19:50 +00:00
{
2016-01-12 06:52:59 +00:00
Directory = Directory.substr(0, nDelimiter);
2016-01-12 12:19:50 +00:00
}
2016-01-12 06:52:59 +00:00
SetDirectory(Directory.c_str());
}
// Task: Set path to current directory
void CPath::CurrentDirectory()
{
char buff_path[260];
2016-01-12 06:52:59 +00:00
memset(buff_path, 0, sizeof(buff_path));
Empty();
#ifdef _WIN32
::GetCurrentDirectoryA(sizeof(buff_path), buff_path);
2016-01-12 06:52:59 +00:00
SetDriveDirectory(buff_path);
#else
getcwd(buff_path, sizeof(buff_path));
SetDirectory(buff_path);
#endif
}
// Task: Set path to the name of specified module
#ifdef _WIN32
2015-10-25 11:10:54 +00:00
void CPath::Module(void * hInstance)
{
2016-01-12 12:19:50 +00:00
char buff_path[MAX_PATH];
2016-01-12 06:52:59 +00:00
memset(buff_path, 0, sizeof(buff_path));
GetModuleFileNameA((HINSTANCE)hInstance, buff_path, MAX_PATH);
2016-01-12 06:52:59 +00:00
m_strPath = buff_path;
}
// Task: Set path to the name of current module
void CPath::Module()
{
2016-01-12 12:19:50 +00:00
Module(m_hInst);
}
// Task: Set path to the directory of specified module
2015-10-25 11:10:54 +00:00
void CPath::ModuleDirectory(void * hInstance)
{
2016-01-12 12:19:50 +00:00
Module(hInstance);
2016-01-12 06:52:59 +00:00
SetNameExtension("");
}
// Task: Set path to the directory of current module
void CPath::ModuleDirectory()
{
2016-01-12 06:52:59 +00:00
Module();
SetNameExtension("");
}
#endif
// Post: Return TRUE if it is a directory
// Task: Check if this path represents a directory
2015-10-25 11:10:54 +00:00
bool CPath::IsDirectory() const
{
// Check if this path has a filename
2015-10-25 11:10:54 +00:00
std::string file_name;
GetNameExtension(file_name);
2016-01-12 06:52:59 +00:00
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.
*/
2015-10-25 11:10:54 +00:00
bool CPath::DirectoryExists() const
{
2022-10-03 08:04:42 +00:00
WriteTrace(TracePath, TraceDebug, "m_strPath = %s", m_strPath.c_str());
#ifdef _WIN32
2016-01-12 06:52:59 +00:00
// Create test path
CPath TestPath(m_strPath.c_str());
2016-01-12 06:52:59 +00:00
std::string DirName;
TestPath.UpDirectory(&DirName);
TestPath.SetNameExtension(DirName.c_str());
WIN32_FIND_DATAA FindData;
HANDLE hFindFile = FindFirstFileA((const char *)TestPath, &FindData); // Find anything
2016-08-03 11:30:46 +00:00
bool res = (hFindFile != INVALID_HANDLE_VALUE);
2022-10-03 08:04:42 +00:00
if (hFindFile != nullptr) // Make sure we close the search
2016-01-12 12:19:50 +00:00
{
2016-01-12 06:52:59 +00:00
FindClose(hFindFile);
2016-01-12 12:19:50 +00:00
}
#else
2022-10-03 08:04:42 +00:00
std::string PathText;
2016-08-03 11:30:46 +00:00
GetDirectory(PathText);
StripTrailingBackslash(PathText);
2022-10-03 08:04:42 +00:00
WriteTrace(TracePath, TraceDebug, "Checking if directory \"%s\" exists", PathText.c_str());
2016-08-03 11:30:46 +00:00
struct stat fileinfo;
2016-08-03 11:30:46 +00:00
bool res = stat(PathText.c_str(), &fileinfo) == 0 && S_ISDIR(fileinfo.st_mode);
#endif
2022-10-03 08:04:42 +00:00
WriteTrace(TracePath, TraceDebug, "Exist = %s", res ? "True" : "False");
2016-08-03 11:30:46 +00:00
return res;
2016-01-12 06:52:59 +00:00
}
// Post: Return TRUE if these is such a file
// Task: Check if file exists
2015-10-25 11:10:54 +00:00
bool CPath::Exists() const
{
#ifdef _WIN32
WIN32_FIND_DATAA FindData;
HANDLE hFindFile = FindFirstFileA(m_strPath.c_str(), &FindData);
bool bSuccess = (hFindFile != INVALID_HANDLE_VALUE);
2022-10-03 08:04:42 +00:00
if (hFindFile != nullptr) // Make sure we close the search
2016-01-12 12:19:50 +00:00
{
2016-01-12 06:52:59 +00:00
FindClose(hFindFile);
2016-01-12 12:19:50 +00:00
}
2016-01-12 06:52:59 +00:00
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);
OPENFILENAMEA openfilename;
char FileName[MAX_PATH];
memset(&FileName, 0, sizeof(FileName));
memset(&openfilename, 0, sizeof(openfilename));
openfilename.lStructSize = sizeof(openfilename);
openfilename.hwndOwner = (HWND)hwndOwner;
openfilename.lpstrFilter = FileFilter;
openfilename.lpstrFile = FileName;
openfilename.lpstrInitialDir = InitialDir;
openfilename.nMaxFile = MAX_PATH;
openfilename.Flags = OFN_HIDEREADONLY | (FileMustExist ? OFN_FILEMUSTEXIST : 0);
bool res = GetOpenFileNameA(&openfilename) != 0;
if (CPath(CURRENT_DIRECTORY) != CurrentDir)
{
CurrentDir.ChangeDirectory();
}
if (!res)
{
return false;
}
m_strPath = FileName;
cleanPathString(m_strPath);
return true;
}
#endif
// Post: Return TRUE on success
// Task: Delete file
2015-10-25 11:10:54 +00:00
bool CPath::Delete(bool bEvenIfReadOnly) const
{
#ifdef _WIN32
uint32_t dwAttr = ::GetFileAttributesA(m_strPath.c_str());
2016-01-12 06:52:59 +00:00
if (dwAttr == (uint32_t)-1)
2016-01-12 12:19:50 +00:00
{
// File does not exist
2016-01-12 12:19:50 +00:00
return false;
}
2016-01-12 06:52:59 +00:00
if (((dwAttr & FILE_ATTRIBUTE_READONLY) == FILE_ATTRIBUTE_READONLY) && !bEvenIfReadOnly)
2016-01-12 12:19:50 +00:00
{
// File is read-only, and we're not allowed to delete it
2016-01-12 12:19:50 +00:00
return false;
}
SetFileAttributesA(m_strPath.c_str(), FILE_ATTRIBUTE_NORMAL);
return DeleteFileA(m_strPath.c_str()) != 0;
#else
2016-08-03 11:30:46 +00:00
return unlink(m_strPath.c_str()) == 0;
#endif
2016-01-12 06:52:59 +00:00
}
/*
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.
*/
2015-10-25 11:10:54 +00:00
bool CPath::CopyTo(const char * lpcszTargetFile, bool bOverwrite)
{
2021-04-12 11:35:39 +00:00
if (lpcszTargetFile == nullptr)
2016-08-03 11:30:46 +00:00
{
return false;
}
2022-10-03 08:04:42 +00:00
WriteTrace(TracePath, TraceDebug, "Copy \"%s\" to \"%s\"", m_strPath.c_str(), lpcszTargetFile);
#ifdef _WIN32
// Check if the target file exists
CPath TargetFile(lpcszTargetFile);
2016-01-12 06:52:59 +00:00
if (TargetFile.Exists())
{
2016-01-12 06:52:59 +00:00
// Yeah there is already such a target file
// Decide if we should overwrite
2016-01-12 06:52:59 +00:00
if (!bOverwrite)
2016-01-12 12:19:50 +00:00
{
return false;
}
// Delete any previous target
2016-01-12 12:19:50 +00:00
if (!TargetFile.Delete(true))
{
return false;
}
}
// CopyFile will set the target's attributes to the same as
// the source after copying
return CopyFileA(m_strPath.c_str(), lpcszTargetFile, !bOverwrite) != 0;
#else
2016-08-03 11:30:46 +00:00
bool res = true;
2022-10-03 08:04:42 +00:00
WriteTrace(TracePath, TraceDebug, "Opening \"%s\" for reading", m_strPath.c_str());
2016-08-03 11:30:46 +00:00
FILE * infile = fopen(m_strPath.c_str(), "rb");
2022-10-03 08:04:42 +00:00
if (infile == nullptr)
2016-08-03 11:30:46 +00:00
{
2022-10-03 08:04:42 +00:00
WriteTrace(TracePath, TraceWarning, "Failed to open m_strPath = %s", m_strPath.c_str());
2016-08-03 11:30:46 +00:00
res = false;
}
else
{
2022-10-03 08:04:42 +00:00
WriteTrace(TracePath, TraceDebug, "Opened \"%s\"", m_strPath.c_str());
2016-08-03 11:30:46 +00:00
}
2021-04-12 11:35:39 +00:00
FILE * outfile = nullptr;
2016-08-03 11:30:46 +00:00
if (res)
{
2022-10-03 08:04:42 +00:00
WriteTrace(TracePath, TraceDebug, "Opening \"%s\" for writing", lpcszTargetFile);
2016-08-03 11:30:46 +00:00
outfile = fopen(lpcszTargetFile, "wb");
2021-04-12 11:35:39 +00:00
if (outfile == nullptr)
2016-08-03 11:30:46 +00:00
{
2022-10-03 08:04:42 +00:00
WriteTrace(TracePath, TraceWarning, "Failed to open m_strPath = %s errno=%d", lpcszTargetFile, errno);
2016-08-03 11:30:46 +00:00
res = false;
}
else
{
2022-10-03 08:04:42 +00:00
WriteTrace(TracePath, TraceDebug, "Opened \"%s\"", lpcszTargetFile);
2016-08-03 11:30:46 +00:00
}
}
if (res)
{
WriteTrace(TracePath, TraceDebug, "Copying data");
2016-08-03 11:30:46 +00:00
while (!feof(infile))
{
char buffer[1024];
2022-10-03 08:04:42 +00:00
size_t bytes = fread(buffer, 1, sizeof(buffer), infile);
2016-08-03 11:30:46 +00:00
if (ferror(infile))
{
WriteTrace(TracePath, TraceWarning, "Failed to read from %s", m_strPath.c_str());
2016-08-03 11:30:46 +00:00
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));
2016-08-03 11:30:46 +00:00
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)
{
2022-10-03 08:04:42 +00:00
if (fchmod(fileno(outfile), ts.st_mode) != 0)
2016-08-03 11:30:46 +00:00
{
WriteTrace(TracePath, TraceWarning, "fchmod failed on %s, errno = %X", lpcszTargetFile, errno);
res = false;
}
}
2021-04-12 11:35:39 +00:00
if (infile != nullptr)
2016-08-03 11:30:46 +00:00
{
fclose(infile);
}
2021-04-12 11:35:39 +00:00
if (outfile != nullptr)
2016-08-03 11:30:46 +00:00
{
fclose(outfile);
}
2022-10-03 08:04:42 +00:00
WriteTrace(TracePath, TraceDebug, "Done, res: %s", res ? "true" : "false");
2016-08-03 11:30:46 +00:00
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
2015-10-25 11:10:54 +00:00
bool CPath::MoveTo(const char * lpcszTargetFile, bool bOverwrite)
{
#ifdef _WIN32
// Check if the target file exists
CPath TargetFile(lpcszTargetFile);
2016-01-12 06:52:59 +00:00
if (TargetFile.Exists())
{
2016-01-12 06:52:59 +00:00
// Yeah there is already such a target file
// Decide if we should overwrite
2016-01-12 06:52:59 +00:00
if (!bOverwrite)
2016-01-12 12:19:50 +00:00
{
return false;
}
// Delete any previous target
2016-01-12 06:52:59 +00:00
if (!TargetFile.Delete(TRUE))
2016-01-12 12:19:50 +00:00
{
return false;
}
}
return MoveFileA(m_strPath.c_str(), lpcszTargetFile) != 0;
#else
return false;
#endif
}
// Post: Return TRUE if attributes match
// Task: Compare finder attributes
2015-10-25 11:10:54 +00:00
bool CPath::AttributesMatch(uint32_t dwTargetAttributes, uint32_t dwFileAttributes)
{
if (dwTargetAttributes == FIND_ATTRIBUTE_ALLFILES)
2016-01-12 06:52:59 +00:00
{
return true;
}
if (dwTargetAttributes == FIND_ATTRIBUTE_FILES)
2016-01-12 06:52:59 +00:00
{
return ((FIND_ATTRIBUTE_SUBDIR & dwFileAttributes) == 0);
2016-01-12 06:52:59 +00:00
}
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_DATAA FindData;
m_hFindFile = FindFirstFileA(m_strPath.c_str(), &FindData);
2016-01-12 06:52:59 +00:00
bGotFile = (m_hFindFile != INVALID_HANDLE_VALUE);
while (bGotFile)
{
// Compare candidate to attributes, and filter out the "." and ".." folders
2016-01-12 06:52:59 +00:00
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);
2016-01-12 06:52:59 +00:00
SetNameExtension(FindData.cFileName);
if ((FIND_ATTRIBUTE_SUBDIR & FindData.dwFileAttributes) != 0)
EnsureTrailingBackslash(m_strPath);
2016-01-12 06:52:59 +00:00
return TRUE;
// Not found a match, get another
2016-01-12 06:52:59 +00:00
LABEL_GetAnother:
bGotFile = FindNextFileA(m_hFindFile, &FindData);
2016-01-12 06:52:59 +00:00
}
#else
std::string Directory, Name, Extension;
GetComponents(&Directory, &Name, &Extension);
m_FindWildcard = Name;
if (!Extension.empty())
{
m_FindWildcard += EXTENSION_DELIMITER;
m_FindWildcard += Extension;
}
2016-01-12 06:52:59 +00:00
m_OpenedDir = opendir(Directory.c_str());
2021-04-12 11:35:39 +00:00
if (m_OpenedDir == nullptr) return false;
return FindNext();
#endif
2016-01-12 12:19:50 +00:00
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
2015-10-25 11:10:54 +00:00
bool CPath::FindNext()
{
#ifdef _WIN32
2021-04-12 11:35:39 +00:00
if (m_hFindFile == nullptr)
2016-01-12 12:19:50 +00:00
{
return false;
}
WIN32_FIND_DATAA FindData;
while (FindNextFileA(m_hFindFile, &FindData) != false)
{ // while(FindNext(...))
2016-01-12 06:52:59 +00:00
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
2016-01-12 06:52:59 +00:00
if (IsDirectory())
{
// Found a directory
UpDirectory();
}
SetNameExtension(FindData.cFileName);
}
if ((_A_SUBDIR & FindData.dwFileAttributes) == _A_SUBDIR)
{
2016-01-12 06:52:59 +00:00
EnsureTrailingBackslash(m_strPath);
}
2016-01-12 06:52:59 +00:00
return TRUE;
}
}
#else
2022-10-03 08:04:42 +00:00
dirent * pEntry;
while ((pEntry = readdir((DIR *)m_OpenedDir)))
{
uint32_t dwFileAttributes = pEntry->d_type == DT_DIR ? FIND_ATTRIBUTE_SUBDIR : FIND_ATTRIBUTE_FILES;
2022-10-03 08:04:42 +00:00
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) ||
2022-10-03 08:04:42 +00:00
strcmp(pEntry->d_name, ".") == 0 ||
strcmp(pEntry->d_name, "..") == 0 ||
!wildcmp(m_FindWildcard.c_str(), pEntry->d_name))
{
2022-10-03 08:04:42 +00:00
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);
2016-08-03 11:30:46 +00:00
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
2016-01-12 12:19:50 +00:00
return false;
}
// Post: Return TRUE on success
// Task: Change current working directory of application to path
2015-10-25 11:10:54 +00:00
bool CPath::ChangeDirectory()
{
#ifdef _WIN32
2016-01-12 06:52:59 +00:00
std::string DriveDirectory;
GetDriveDirectory(DriveDirectory);
return SetCurrentDirectoryA(DriveDirectory.c_str()) != 0;
#else
std::string Dir;
GetDirectory(Dir);
return chdir(Dir.c_str()) == 0;
#endif
}
2018-11-19 10:46:58 +00:00
void CPath::NormalizePath(CPath BaseDir)
{
#ifdef _WIN32
2022-10-03 08:04:42 +00:00
stdstr Directory = BaseDir.GetDriveDirectory();
2018-11-19 10:46:58 +00:00
#else
2022-10-03 08:04:42 +00:00
stdstr Directory = BaseDir.GetDirectory();
2018-11-19 10:46:58 +00:00
#endif
2022-10-03 08:04:42 +00:00
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;
}
2018-11-19 10:46:58 +00:00
#ifdef _WIN32
2022-10-03 08:04:42 +00:00
SetDriveDirectory(Directory.c_str());
2018-11-19 10:46:58 +00:00
#else
2022-10-03 08:04:42 +00:00
SetDirectory(Directory.c_str());
2018-11-19 10:46:58 +00:00
#endif
2022-10-03 08:04:42 +00:00
}
2018-11-19 10:46:58 +00:00
}
// Pre: If bCreateIntermediates is TRUE, create all eventually missing parent directories too
// Post: Return TRUE on success
// Task: Create new directory
2015-10-25 11:10:54 +00:00
bool CPath::DirectoryCreate(bool bCreateIntermediates /*= TRUE*/)
{
2022-10-03 08:04:42 +00:00
WriteTrace(TracePath, TraceDebug, "m_strPath = %s bCreateIntermediates = %s", m_strPath.c_str(), bCreateIntermediates ? "true" : "false");
std::string PathText;
bool bSuccess;
2016-01-12 06:52:59 +00:00
2016-08-03 11:30:46 +00:00
if (DirectoryExists())
{
WriteTrace(TracePath, TraceDebug, "Directory already exists, res = true");
return true;
}
#ifdef _WIN32
2016-01-12 06:52:59 +00:00
GetDriveDirectory(PathText);
StripTrailingBackslash(PathText);
2022-10-03 08:04:42 +00:00
WriteTrace(TracePath, TraceDebug, "Create %s", PathText.c_str());
2021-04-12 11:35:39 +00:00
bSuccess = ::CreateDirectoryA(PathText.c_str(), nullptr) != 0;
#else
GetDirectory(PathText);
StripTrailingBackslash(PathText);
2022-10-03 08:04:42 +00:00
WriteTrace(TracePath, TraceDebug, "Create %s", PathText.c_str());
2016-08-03 11:30:46 +00:00
bSuccess = mkdir(PathText.c_str(), S_IRWXU) == 0;
if (!bSuccess)
2016-01-12 06:52:59 +00:00
{
2022-10-03 08:04:42 +00:00
WriteTrace(TracePath, TraceWarning, "Failed to create \"%s\" errno: %d", PathText.c_str(), errno);
2016-01-12 06:52:59 +00:00
}
#endif
2016-01-12 06:52:59 +00:00
if (!bSuccess && bCreateIntermediates)
{
WriteTrace(TracePath, TraceDebug, "Failed creating intermediates");
2016-01-12 06:52:59 +00:00
std::string::size_type nDelimiter = PathText.rfind(DIRECTORY_DELIMITER);
if (nDelimiter == std::string::npos)
2016-01-12 12:19:50 +00:00
{
return false;
}
2016-01-12 06:52:59 +00:00
PathText.resize(nDelimiter + 1);
CPath SubPath(PathText);
2016-01-12 12:19:50 +00:00
return SubPath.DirectoryCreate() ? DirectoryCreate(false) : false;
2016-01-12 06:52:59 +00:00
}
2022-10-03 08:04:42 +00:00
WriteTrace(TracePath, TraceDebug, "res = %s", bSuccess ? "true" : "false");
2016-01-12 06:52:59 +00:00
return bSuccess;
}
// Helpers
// Task: Remove first character (if any) if it's chLeading
2022-10-03 08:04:42 +00:00
void CPath::cleanPathString(std::string & rDirectory) const
{
2016-01-12 06:52:59 +00:00
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);
}
2016-01-12 12:19:50 +00:00
bool AppendEnd = !_strnicmp(rDirectory.c_str(), DIR_DOUBLEDELIM, 2);
2016-01-12 06:52:59 +00:00
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());
2016-01-12 06:52:59 +00:00
}
}
2022-10-03 08:04:42 +00:00
void CPath::StripLeadingChar(std::string & rText, char chLeading) const
{
2016-01-12 06:52:59 +00:00
std::string::size_type nLength = rText.length();
if (nLength == 0)
return;
2016-01-12 06:52:59 +00:00
if (rText[0] == chLeading)
rText = rText.substr(1);
}
// Task: Remove first character if '\'
2022-10-03 08:04:42 +00:00
void CPath::StripLeadingBackslash(std::string & Directory) const
{
2016-01-12 06:52:59 +00:00
std::string::size_type nLength = Directory.length();
// If directory is of the form '\', don't do it
2016-01-12 06:52:59 +00:00
if (nLength <= 1)
return;
2016-01-12 06:52:59 +00:00
if (Directory[0] == DIRECTORY_DELIMITER)
Directory = Directory.substr(1);
}
// Task: Remove last character (if any) if it's chTrailing
2022-10-03 08:04:42 +00:00
void CPath::StripTrailingChar(std::string & rText, char chTrailing) const
{
2016-01-12 06:52:59 +00:00
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 '\'
2022-10-03 08:04:42 +00:00
void CPath::StripTrailingBackslash(std::string & rDirectory) const
{
2016-01-12 06:52:59 +00:00
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
2022-10-03 08:04:42 +00:00
void CPath::EnsureTrailingBackslash(std::string & Directory) const
{
2016-01-12 06:52:59 +00:00
std::string::size_type nLength = Directory.length();
2016-01-12 06:52:59 +00:00
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
2015-10-25 11:10:54 +00:00
void CPath::EnsureLeadingBackslash(std::string & Directory) const
{
2016-01-12 06:52:59 +00:00
if (Directory.empty() || (Directory[0] != DIRECTORY_DELIMITER))
{
2016-01-12 06:52:59 +00:00
Directory = stdstr_f("%c%s", DIRECTORY_DELIMITER, Directory.c_str());
}
}
#ifndef _WIN32
2022-10-03 08:04:42 +00:00
bool CPath::wildcmp(const char * wild, const char * string)
{
2021-04-12 11:35:39 +00:00
const char *cp = nullptr, *mp = nullptr;
2016-08-03 11:30:46 +00:00
while ((*string) && (*wild != '*'))
{
2016-08-03 11:30:46 +00:00
if ((*wild != *string) && (*wild != '?'))
{
return 0;
}
wild++;
string++;
}
2016-08-03 11:30:46 +00:00
while (*string)
{
2016-08-03 11:30:46 +00:00
if (*wild == '*')
{
2016-08-03 11:30:46 +00:00
if (!*++wild)
{
return 1;
}
mp = wild;
2022-10-03 08:04:42 +00:00
cp = string + 1;
2016-08-03 11:30:46 +00:00
}
else if ((*wild == *string) || (*wild == '?'))
{
wild++;
string++;
2016-08-03 11:30:46 +00:00
}
else
{
wild = mp;
string = cp++;
}
}
2016-08-03 11:30:46 +00:00
while (*wild == '*')
{
wild++;
}
return !*wild;
}
#endif