3#define _WIN32_IE 0x0500
32#define MAXPATHLEN PATH_MAX
33#elif defined(MAX_PATH)
34#define MAXPATHLEN MAX_PATH
36#define MAXPATHLEN 4096
67#include "SDL_system.h"
71using std::string_view;
81 return !path.empty() && (path[0] ==
'~');
89 auto pos = path.find_first_of(
'/');
90 string_view user = ((path.size() == 1) || (pos == 1))
92 : string_view(path).substr(1, (pos == string::npos) ? pos : pos - 1);
98 if (pos == string_view::npos) {
101 if (result.back() !=
'/') {
104 string_view last = string_view(path).substr(pos + 1);
105 result.append(last.data(), last.size());
120 if (result && (errno != EEXIST)) {
125static bool isUNCPath(string_view path)
128 return path.starts_with(
"//") || path.starts_with(
"\\\\");
152 int skip = isUNCPath(path) ? 2 :
154 string::size_type pos = 0;
156 pos = path.find_first_of(
'/', pos + 1);
161 mkdir(path.substr(0, pos), 0755);
162 }
while (pos != string::npos);
172 return _wunlink(
utf8to16(path).c_str());
181 return _wrmdir(
utf8to16(path).c_str());
190 std::wstring pathW =
utf8to16(path);
192 SHFILEOPSTRUCTW rmdirFileOp;
193 rmdirFileOp.hwnd =
nullptr;
194 rmdirFileOp.wFunc = FO_DELETE;
195 rmdirFileOp.pFrom = pathW.c_str();
196 rmdirFileOp.pTo =
nullptr;
197 rmdirFileOp.fFlags = FOF_SILENT | FOF_NOCONFIRMATION | FOF_NOERRORUI;
198 rmdirFileOp.fAnyOperationsAborted = FALSE;
199 rmdirFileOp.hNameMappings =
nullptr;
200 rmdirFileOp.lpszProgressTitle =
nullptr;
202 return SHFileOperationW(&rmdirFileOp);
205static int deleteRecursive_cb(
const char* fpath,
const struct stat* ,
212 return nftw(path.
c_str(), deleteRecursive_cb, 64, FTW_DEPTH | FTW_PHYS);
228 while (dirent* d = dir.getEntry()) {
245 assert(mode.
find(
'b') != std::string::npos);
256#if defined _WIN32 && defined _MSC_VER
259 stream.open(
utf8to16(filename).c_str());
261 stream.open(filename.
c_str());
266 std::ios_base::openmode mode)
268#if defined _WIN32 && defined _MSC_VER
271 stream.open(
utf8to16(filename).c_str(), mode);
273 stream.open(filename.
c_str(), mode);
279 if (
auto pos = path.rfind(
'/'); pos != string_view::npos) {
280 return path.substr(pos + 1);
287 if (
auto pos = path.rfind(
'/'); pos != string_view::npos) {
288 return path.substr(0, pos + 1);
296 if (
auto pos = filename.rfind(
'.'); pos != string_view::npos) {
297 return filename.substr(pos);
304 if (
auto pos = path.rfind(
'.'); pos != string_view::npos) {
305 return path.substr(0, pos);
310string join(string_view part1, string_view part2)
313 return string(part2);
315 if (part1.back() ==
'/') {
316 return strCat(part1, part2);
318 return strCat(part1,
'/', part2);
320string join(string_view part1, string_view part2, string_view part3)
322 return join(part1,
join(part2, part3));
325string join(string_view part1, string_view part2,
326 string_view part3, string_view part4)
351 throw FileException(
"Couldn't get current working directory.");
355 std::array<char, MAXPATHLEN> buf;
356 char* result = getcwd(buf.data(), buf.size());
358 throw FileException(
"Couldn't get current working directory.");
376 if (isUNCPath(path))
return true;
378 if ((path.size() >= 3) && (path[1] ==
':') && (path[2] ==
one_of(
'/',
'\\'))) {
379 char drive = tolower(path[0]);
380 if ((
'a' <= drive) && (drive <=
'z')) {
385 return !path.empty() && (path[0] ==
'/');
394 if (!SHGetSpecialFolderPathW(
nullptr, bufW, CSIDL_PERSONAL, TRUE)) {
396 "SHGetSpecialFolderPathW failed: ", GetLastError());
401 const char* dir =
nullptr;
402 struct passwd* pw =
nullptr;
403 if (username.empty()) {
404 dir = getenv(
"HOME");
406 pw = getpwuid(getuid());
409 pw = getpwnam(
string(username).c_str());
414 return dir ? dir :
string{};
420 static const string OPENMSX_DIR = []() ->
string {
421 if (
const char* home = getenv(
"OPENMSX_HOME")) {
426#elif PLATFORM_ANDROID
429 return strCat(SDL_AndroidGetExternalStoragePath(),
"/openMSX");
439 static std::optional<string> result;
441 const char*
const NAME =
"OPENMSX_USER_DATA";
442 char* value = getenv(NAME);
450 static std::optional<string> result;
451 if (!result) result = []() ->
string {
452 if (
char* value = getenv(
"OPENMSX_SYSTEM_DATA")) {
457 int res = GetModuleFileNameW(
nullptr, bufW, DWORD(
std::size(bufW)));
460 "Cannot detect openMSX directory. GetModuleFileNameW failed: ",
465 auto pos = filename.find_last_of(
'\\');
466 if (pos == string::npos) {
467 throw FatalError(
"openMSX is not in directory!?");
470#elif defined(__APPLE__)
472#elif PLATFORM_ANDROID
483static bool driveExists(
char driveLetter)
485 char buf[] = { driveLetter,
':', 0 };
486 return GetFileAttributesA(buf) != INVALID_FILE_ATTRIBUTES;
493 string result = path;
494 if (((path.size() == 2) && (path[1] ==
':')) ||
495 ((path.size() >= 3) && (path[1] ==
':') && (path[2] !=
'/'))) {
497 unsigned char drive = tolower(path[0]);
498 if ((
'a' <= drive) && (drive <=
'z')) {
500 if (driveExists(drive) &&
501 _wgetdcwd(drive -
'a' + 1, bufW,
MAXPATHLEN)) {
503 if (result.back() !=
'/') {
506 if (path.size() > 2) {
507 string_view tmp = path.substr(2);
508 result.append(tmp.data(), tmp.size());
519 std::optional<Stat> st;
523 std::string filename2(filename);
526 if (
auto pos = filename2.find_last_not_of(
'/'); pos != string::npos) {
527 filename2.resize(pos + 1);
530 if (!filename2.empty()) filename2.resize(1);
532 if (_wstat(
utf8to16(filename2).c_str(), &*st)) {
536 if (stat(filename.
c_str(), &*st)) {
546 return S_ISREG(st.st_mode);
556 return S_ISDIR(st.st_mode);
567 return static_cast<bool>(
getStat(filename));
570static unsigned getNextNum(dirent* d, string_view prefix, string_view extension,
573 auto extensionLen = extension.size();
574 auto prefixLen = prefix.size();
575 string_view name(d->d_name);
577 if ((name.size() != (prefixLen + nofDigits + extensionLen)) ||
578 (name.substr(0, prefixLen) != prefix) ||
579 (name.substr(prefixLen + nofDigits, extensionLen) != extension)) {
582 if (
auto n = StringOp::stringToBase<10, unsigned>(name.substr(prefixLen, nofDigits))) {
589 string_view directory, string_view prefix, string_view extension)
591 const unsigned nofDigits = 4;
593 unsigned max_num = 0;
604 max_num =
std::max(max_num, getNextNum(d, prefix, extension, nofDigits));
607 std::ostringstream os;
608 os << dirName <<
'/' << prefix;
611 os << (max_num + 1) << extension;
616 string_view argument, string_view directory,
617 string_view prefix, string_view extension)
619 if (argument.empty()) {
624 string filename(argument);
629 filename =
strCat(dir,
'/', filename);
634 if (!filename.ends_with(extension) && !
exists(filename)) {
639 filename.append(extension.data(), extension.size());
647 DWORD len = GetTempPathW(0,
nullptr);
649 VLA(
wchar_t, bufW, (len + 1));
650 len = GetTempPathW(len, bufW.data());
653 if (bufW[len - 1] ==
L'\\') {
654 bufW[len - 1] =
L'\0';
659 throw FatalError(
"GetTempPathW failed: ", GetLastError());
660#elif PLATFORM_ANDROID
664 const char* result =
nullptr;
665 if (!result) result = getenv(
"TMPDIR");
666 if (!result) result = getenv(
"TMP");
667 if (!result) result = getenv(
"TEMP");
678 std::wstring directoryW =
utf8to16(directory);
679 wchar_t filenameW[MAX_PATH];
680 if (!GetTempFileNameW(directoryW.c_str(),
L"msx", 0, filenameW)) {
681 throw FileException(
"GetTempFileNameW failed: ", GetLastError());
684 return FILE_t(_wfopen(filenameW,
L"wb"));
686 filename = directory +
"/XXXXXX";
687 auto oldMask = umask(S_IRWXO | S_IRWXG);
688 int fd = mkstemp(
const_cast<char*
>(filename.c_str()));
693 return FILE_t(fdopen(fd,
"wb"));
Simple wrapper around opendir() / readdir() / closedir() functions.
struct dirent * getEntry()
Get directory entry for next file.
Like std::string_view, but with the extra guarantee that it refers to a zero-terminated string.
constexpr bool ends_with(std::string_view sv) const
constexpr const char * c_str() const
constexpr auto find(char c, size_type pos=0) const
constexpr vecN< N, T > max(const vecN< N, T > &x, const vecN< N, T > &y)
string parseCommandFileArgument(string_view argument, string_view directory, string_view prefix, string_view extension)
Helper function for parsing filename arguments in Tcl commands.
std::string findShareDir()
bool exists(zstring_view filename)
Does this file (directory) exists?
FILE_t openUniqueFile(const std::string &directory, std::string &filename)
Open a new file with a unique name in the provided directory.
void mkdir(zstring_view path, mode_t mode)
Create the specified directory.
string expandTilde(string path)
Expand the '~' character to the users home directory.
string_view getDirName(string_view path)
Returns the directory portion of a path.
const std::string & getNativePath(const std::string &path)
Returns the path in native path-delimiter.
string getCurrentWorkingDirectory()
Returns the current working directory.
const std::string & expandCurrentDirFromDrive(const std::string &path)
Get the current directory of the specified drive Linux: return the given string unchanged.
string getUserHomeDir(string_view username)
Get user's home directory.
bool isRegularFile(const Stat &st)
string_view getExtension(string_view path)
Returns the extension portion of a path.
const string & getUserOpenMSXDir()
Get the openMSX dir in the user's home directory.
int rmdir(zstring_view path)
Call rmdir() in a platform-independent manner.
string getAbsolutePath(string_view path)
Transform given path into an absolute path.
string_view stripExtension(string_view path)
Returns the path without extension.
string getTempDir()
Get the name of the temp directory on the system.
bool isDirectory(const Stat &st)
void openOfStream(std::ofstream &stream, zstring_view filename)
Open an ofstream in a platform-independent manner.
bool isAbsolutePath(string_view path)
Checks whether it's a absolute path or not.
string_view getFilename(string_view path)
Returns the file portion of a path name.
const string & getSystemDataDir()
Get system directory.
std::optional< Stat > getStat(zstring_view filename)
Call stat() and return the stat structure.
FILE_t openFile(zstring_view filename, zstring_view mode)
Call fopen() in a platform-independent manner.
bool needsTildeExpansion(std::string_view path)
Returns true iff expandTilde(s) would have an effect.
int deleteRecursive(zstring_view path)
Recursively delete a file or directory and (in case of a directory) all its sub-components.
const std::string & getConventionalPath(const std::string &path)
Returns the path in conventional path-delimiter.
string getNextNumberedFileName(string_view directory, string_view prefix, string_view extension)
Gets the next numbered file name with the specified prefix in the specified directory,...
void mkdirp(string path)
Acts like the unix command "mkdir -p".
const string & getUserDataDir()
Get the openMSX data dir in the user's home directory.
std::unique_ptr< FILE, FClose > FILE_t
int unlink(zstring_view path)
Call unlink() in a platform-independent manner.
string join(string_view part1, string_view part2)
Join two paths.
auto remove(ForwardRange &&range, const T &value)
constexpr void replace(ForwardRange &&range, const T &old_value, const T &new_value)
size_t size(std::string_view utf8)
u16bit_iterator utf8to16(octet_iterator start, octet_iterator end, u16bit_iterator result)
octet_iterator utf16to8(u16bit_iterator start, u16bit_iterator end, octet_iterator result)
std::string strCat(Ts &&...ts)
#define VLA(TYPE, NAME, LENGTH)