3 #define _WIN32_IE 0x0500 // For SHGetSpecialFolderPathW with MinGW
16 #else // ifdef _WIN32_ ...
17 #include <sys/types.h>
21 #endif // ifdef _WIN32_ ... else ...
32 #define MAXPATHLEN PATH_MAX
33 #elif defined(MAX_PATH)
34 #define MAXPATHLEN MAX_PATH
36 #define MAXPATHLEN 4096
67 #include "SDL_system.h"
71 using 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());
122 if (result && (errno != EEXIST)) {
127 static bool isUNCPath(string_view path)
154 int skip = isUNCPath(path) ? 2 :
156 string::size_type pos = 0;
158 pos = path.find_first_of(
'/', pos + 1);
163 mkdir(path.substr(0, pos), 0755);
164 }
while (pos != string::npos);
174 return _wunlink(
utf8to16(path).c_str());
183 return _wrmdir(
utf8to16(path).c_str());
192 std::wstring pathW =
utf8to16(path);
194 SHFILEOPSTRUCTW rmdirFileOp;
195 rmdirFileOp.hwnd =
nullptr;
196 rmdirFileOp.wFunc = FO_DELETE;
197 rmdirFileOp.pFrom = pathW.c_str();
198 rmdirFileOp.pTo =
nullptr;
199 rmdirFileOp.fFlags = FOF_SILENT | FOF_NOCONFIRMATION | FOF_NOERRORUI;
200 rmdirFileOp.fAnyOperationsAborted = FALSE;
201 rmdirFileOp.hNameMappings =
nullptr;
202 rmdirFileOp.lpszProgressTitle =
nullptr;
204 return SHFileOperationW(&rmdirFileOp);
207 static int deleteRecursive_cb(
const char* fpath,
const struct stat* ,
214 return nftw(path.
c_str(), deleteRecursive_cb, 64, FTW_DEPTH | FTW_PHYS);
230 while (dirent* d = dir.getEntry()) {
247 assert(mode.
find(
'b') != std::string::npos);
258 #if defined _WIN32 && defined _MSC_VER
268 std::ios_base::openmode mode)
270 #if defined _WIN32 && defined _MSC_VER
275 stream.open(
filename.c_str(), mode);
281 if (
auto pos = path.rfind(
'/'); pos != string_view::npos) {
282 return path.substr(pos + 1);
289 if (
auto pos = path.rfind(
'/'); pos != string_view::npos) {
290 return path.substr(0, pos + 1);
298 if (
auto pos =
filename.rfind(
'.'); pos != string_view::npos) {
306 if (
auto pos = path.rfind(
'.'); pos != string_view::npos) {
307 return path.substr(0, pos);
312 string join(string_view part1, string_view part2)
315 return string(part2);
317 if (part1.back() ==
'/') {
318 return strCat(part1, part2);
320 return strCat(part1,
'/', part2);
322 string join(string_view part1, string_view part2, string_view part3)
324 return join(part1,
join(part2, part3));
327 string join(string_view part1, string_view part2,
328 string_view part3, string_view part4)
361 throw FileException(
"Couldn't get current working directory.");
378 if (isUNCPath(path))
return true;
380 if ((path.size() >= 3) && (path[1] ==
':') && (path[2] ==
one_of(
'/',
'\\'))) {
381 char drive = tolower(path[0]);
382 if ((
'a' <= drive) && (drive <=
'z')) {
387 return !path.empty() && (path[0] ==
'/');
396 if (!SHGetSpecialFolderPathW(
nullptr, bufW, CSIDL_PERSONAL, TRUE)) {
398 "SHGetSpecialFolderPathW failed: ", GetLastError());
403 const char* dir =
nullptr;
404 struct passwd* pw =
nullptr;
405 if (username.empty()) {
406 dir = getenv(
"HOME");
408 pw = getpwuid(getuid());
411 pw = getpwnam(
string(username).c_str());
416 return dir ? dir :
string{};
423 static const string OPENMSX_DIR =
expandTilde(
"~/openMSX");
424 #elif PLATFORM_ANDROID
427 static const string OPENMSX_DIR =
strCat(SDL_AndroidGetExternalStoragePath(),
"/openMSX");
429 static const string OPENMSX_DIR =
expandTilde(
"~/.openMSX");
436 static std::optional<string> result;
438 const char*
const NAME =
"OPENMSX_USER_DATA";
439 char* value = getenv(NAME);
447 static std::optional<string> result;
448 if (!result) result = []() ->
string {
449 if (
char* value = getenv(
"OPENMSX_SYSTEM_DATA")) {
454 int res = GetModuleFileNameW(
nullptr, bufW, DWORD(
std::size(bufW)));
457 "Cannot detect openMSX directory. GetModuleFileNameW failed: ",
462 auto pos =
filename.find_last_of(
'\\');
463 if (pos == string::npos) {
464 throw FatalError(
"openMSX is not in directory!?");
467 #elif defined(__APPLE__)
469 #elif PLATFORM_ANDROID
480 static bool driveExists(
char driveLetter)
482 char buf[] = { driveLetter,
':', 0 };
483 return GetFileAttributesA(buf) != INVALID_FILE_ATTRIBUTES;
490 string result = path;
491 if (((path.size() == 2) && (path[1] ==
':')) ||
492 ((path.size() >= 3) && (path[1] ==
':') && (path[2] !=
'/'))) {
494 unsigned char drive = tolower(path[0]);
495 if ((
'a' <= drive) && (drive <=
'z')) {
497 if (driveExists(drive) &&
498 _wgetdcwd(drive -
'a' + 1, bufW,
MAXPATHLEN)) {
500 if (result.back() !=
'/') {
503 if (path.size() > 2) {
504 string_view tmp = path.substr(2);
505 result.append(tmp.data(), tmp.size());
520 if (
auto pos = filename2.find_last_not_of(
'/'); pos != string::npos) {
521 filename2.resize(pos + 1);
524 if (!filename2.empty()) filename2.resize(1);
526 return _wstat(
utf8to16(filename2).c_str(), &st) == 0;
528 return stat(
filename.c_str(), &st) == 0;
534 return S_ISREG(st.st_mode);
544 return S_ISDIR(st.st_mode);
559 static unsigned getNextNum(dirent* d, string_view prefix, string_view extension,
562 auto extensionLen = extension.size();
563 auto prefixLen = prefix.size();
564 string_view name(d->d_name);
566 if ((name.size() != (prefixLen + nofdigits + extensionLen)) ||
567 (name.substr(0, prefixLen) != prefix) ||
568 (name.substr(prefixLen + nofdigits, extensionLen) != extension)) {
571 if (
auto n = StringOp::stringToBase<10, unsigned>(name.substr(prefixLen, nofdigits))) {
578 string_view directory, string_view prefix, string_view extension)
580 const unsigned nofdigits = 4;
582 unsigned max_num = 0;
593 max_num =
std::max(max_num, getNextNum(d, prefix, extension, nofdigits));
596 std::ostringstream os;
597 os << dirName <<
'/' << prefix;
600 os << (max_num + 1) << extension;
605 string_view argument, string_view directory,
606 string_view prefix, string_view extension)
608 if (argument.empty()) {
629 filename.append(extension.data(), extension.size());
637 DWORD len = GetTempPathW(0,
nullptr);
639 VLA(
wchar_t, bufW, (len+1));
640 len = GetTempPathW(len, bufW);
643 if (bufW[len-1] ==
L'\\') {
649 throw FatalError(
"GetTempPathW failed: ", GetLastError());
650 #elif PLATFORM_ANDROID
654 const char* result =
nullptr;
655 if (!result) result = getenv(
"TMPDIR");
656 if (!result) result = getenv(
"TMP");
657 if (!result) result = getenv(
"TEMP");
668 std::wstring directoryW =
utf8to16(directory);
669 wchar_t filenameW[MAX_PATH];
670 if (!GetTempFileNameW(directoryW.c_str(),
L"msx", 0, filenameW)) {
671 throw FileException(
"GetTempFileNameW failed: ", GetLastError());
674 return FILE_t(_wfopen(filenameW,
L"wb"));
677 auto oldMask = umask(S_IRWXO | S_IRWXG);
678 int fd = mkstemp(
const_cast<char*
>(
filename.c_str()));
683 return FILE_t(fdopen(fd,
"wb"));