3#define _WIN32_IE 0x0500
31#define MAXPATHLEN PATH_MAX
32#elif defined(MAX_PATH)
33#define MAXPATHLEN MAX_PATH
35#define MAXPATHLEN 4096
66#include "SDL_system.h"
70using std::string_view;
80 return !path.empty() && (path[0] ==
'~');
88 auto pos = path.find_first_of(
'/');
89 string_view user = ((path.size() == 1) || (pos == 1))
91 : string_view(path).substr(1, (pos == string::npos) ? pos : pos - 1);
97 if (pos == string_view::npos) {
100 if (result.back() !=
'/') {
103 string_view last = string_view(path).substr(pos + 1);
104 result.append(last.data(), last.size());
115 int result = _wmkdir(utf8to16(
getNativePath(
string(path))).c_str());
119 if (result && (errno != EEXIST)) {
124static bool isUNCPath(string_view path)
127 return path.starts_with(
"//") || path.starts_with(
"\\\\");
151 int skip = isUNCPath(path) ? 2 :
153 string::size_type pos = 0;
155 pos = path.find_first_of(
'/', pos + 1);
160 mkdir(path.substr(0, pos), 0755);
161 }
while (pos != string::npos);
171 return _wunlink(utf8to16(path).c_str());
173 return ::unlink(path.
c_str());
180 return _wrmdir(utf8to16(path).c_str());
182 return ::rmdir(path.
c_str());
189 std::wstring pathW = utf8to16(path);
191 SHFILEOPSTRUCTW rmdirFileOp;
192 rmdirFileOp.hwnd =
nullptr;
193 rmdirFileOp.wFunc = FO_DELETE;
194 rmdirFileOp.pFrom = pathW.c_str();
195 rmdirFileOp.pTo =
nullptr;
196 rmdirFileOp.fFlags = FOF_SILENT | FOF_NOCONFIRMATION | FOF_NOERRORUI;
197 rmdirFileOp.fAnyOperationsAborted = FALSE;
198 rmdirFileOp.hNameMappings =
nullptr;
199 rmdirFileOp.lpszProgressTitle =
nullptr;
201 return SHFileOperationW(&rmdirFileOp);
204static int deleteRecursive_cb(
const char* fpath,
const struct stat* ,
207 return remove(fpath);
211 return nftw(path.
c_str(), deleteRecursive_cb, 64, FTW_DEPTH | FTW_PHYS);
227 while (dirent* d = dir.
getEntry()) {
244 assert(mode.
find(
'b') != std::string::npos);
246 return FILE_t(_wfopen(utf8to16(filename).c_str(),
247 utf8to16(mode).c_str()));
255#if defined _WIN32 && defined _MSC_VER
258 stream.open(utf8to16(filename).c_str());
260 stream.open(filename.
c_str());
265 std::ios_base::openmode mode)
267#if defined _WIN32 && defined _MSC_VER
270 stream.open(utf8to16(filename).c_str(), mode);
272 stream.open(filename.
c_str(), mode);
278 if (
auto pos = path.rfind(
'/'); pos != string_view::npos) {
279 return path.substr(pos + 1);
286 if (
auto pos = path.rfind(
'/'); pos != string_view::npos) {
287 return path.substr(0, pos + 1);
295 if (
auto pos = filename.rfind(
'.'); pos != string_view::npos) {
296 return filename.substr(pos);
303 if (
auto pos = path.rfind(
'.'); pos != string_view::npos) {
304 return path.substr(0, pos);
309string join(string_view part1, string_view part2)
312 return string(part2);
314 if (part1.back() ==
'/') {
315 return strCat(part1, part2);
317 return strCat(part1,
'/', part2);
319string join(string_view part1, string_view part2, string_view part3)
321 return join(part1,
join(part2, part3));
324string join(string_view part1, string_view part2,
325 string_view part3, string_view part4)
347 std::array<wchar_t, MAXPATHLEN> bufW;
348 const wchar_t* result = _wgetcwd(bufW.data(), bufW.size());
350 throw FileException(
"Couldn't get current working directory.");
352 return utf16to8(result);
354 std::array<char, MAXPATHLEN> buf;
355 char* result = getcwd(buf.data(), buf.size());
357 throw FileException(
"Couldn't get current working directory.");
375 if (isUNCPath(path))
return true;
377 if ((path.size() >= 3) && (path[1] ==
':') && (path[2] ==
one_of(
'/',
'\\'))) {
378 char drive = tolower(path[0]);
379 if ((
'a' <= drive) && (drive <=
'z')) {
384 return !path.empty() && (path[0] ==
'/');
392 std::array<wchar_t, MAXPATHLEN + 1> bufW;
393 if (!SHGetSpecialFolderPathW(
nullptr, bufW.data(), CSIDL_PERSONAL, TRUE)) {
395 "SHGetSpecialFolderPathW failed: ", GetLastError());
400 const char* dir =
nullptr;
401 struct passwd* pw =
nullptr;
402 if (username.empty()) {
403 dir = getenv(
"HOME");
405 pw = getpwuid(getuid());
408 pw = getpwnam(
string(username).c_str());
413 return dir ? dir :
string{};
419 static const string OPENMSX_DIR = []() ->
string {
420 if (
const char* home = getenv(
"OPENMSX_HOME")) {
425#elif PLATFORM_ANDROID
428 return strCat(SDL_AndroidGetExternalStoragePath(),
"/openMSX");
438 static std::optional<string> result;
440 const char*
const NAME =
"OPENMSX_USER_DATA";
441 const char* value = getenv(NAME);
449 static std::optional<string> result;
450 if (!result) result = []() ->
string {
451 if (
const char* value = getenv(
"OPENMSX_SYSTEM_DATA")) {
455 std::array<wchar_t, MAXPATHLEN + 1> bufW;
456 if (
int res = GetModuleFileNameW(
nullptr, bufW.data(), DWORD(bufW.size()));
459 "Cannot detect openMSX directory. GetModuleFileNameW failed: ",
463 string filename = utf16to8(bufW.data());
464 auto pos = filename.find_last_of(
'\\');
465 if (pos == string::npos) {
466 throw FatalError(
"openMSX is not in directory!?");
469#elif defined(__APPLE__)
471#elif PLATFORM_ANDROID
483 static std::optional<string> result;
484 if (!result) result = []() ->
string {
486 std::array<wchar_t, MAXPATHLEN + 1> bufW;
487 if (
int res = GetModuleFileNameW(
nullptr, bufW.data(), DWORD(bufW.size()));
490 "Cannot detect openMSX directory. GetModuleFileNameW failed: ",
494 string filename = utf16to8(bufW.data());
495 auto pos = filename.find_last_of(
'\\');
496 if (pos == string::npos) {
497 throw FatalError(
"openMSX is not in directory!?");
500#elif defined(__APPLE__)
502#elif PLATFORM_ANDROID
513static bool driveExists(
char driveLetter)
515 std::array<char, 3> buf = {driveLetter,
':', 0};
516 return GetFileAttributesA(buf.data()) != INVALID_FILE_ATTRIBUTES;
523 string result = path;
524 if (((path.size() == 2) && (path[1] ==
':')) ||
525 ((path.size() >= 3) && (path[1] ==
':') && (path[2] !=
'/'))) {
527 unsigned char drive = tolower(path[0]);
528 if ((
'a' <= drive) && (drive <=
'z')) {
529 std::array<wchar_t, MAXPATHLEN + 1> bufW;
530 if (driveExists(drive) &&
531 _wgetdcwd(drive -
'a' + 1, bufW.data(),
MAXPATHLEN)) {
533 if (result.back() !=
'/') {
536 if (path.size() > 2) {
537 auto tmp = std::string_view(path).substr(2);
538 result.append(tmp.data(), tmp.size());
549 std::optional<Stat> st;
553 std::string filename2(filename);
556 if (
auto pos = filename2.find_last_not_of(
'/'); pos != string::npos) {
557 filename2.resize(pos + 1);
560 if (!filename2.empty()) filename2.resize(1);
562 if (_wstat(utf8to16(filename2).c_str(), &*st)) {
566 if (stat(filename.
c_str(), &*st)) {
576 return S_ISREG(st.st_mode);
586 return S_ISDIR(st.st_mode);
597 return static_cast<bool>(
getStat(filename));
600static unsigned getNextNum(
const dirent* d, string_view prefix, string_view extension,
603 auto extensionLen = extension.size();
604 auto prefixLen = prefix.size();
605 string_view name(d->d_name);
607 if ((name.size() != (prefixLen + nofDigits + extensionLen)) ||
608 (!name.starts_with(prefix)) ||
609 (name.substr(prefixLen + nofDigits, extensionLen) != extension)) {
612 return StringOp::stringToBase<10, unsigned>(name.substr(prefixLen, nofDigits)).value_or(0);
616 string_view directory, string_view prefix, string_view extension,
bool addSeparator)
618 std::string newPrefix;
620 newPrefix =
strCat(prefix, ((prefix.find(
' ') != std::string_view::npos) ?
' ' :
'_'));
624 const unsigned nofDigits = 4;
626 unsigned max_num = 0;
637 max_num = std::max(max_num, getNextNum(d, prefix, extension, nofDigits));
640 std::ostringstream os;
644 os << (max_num + 1) << extension;
649 string_view argument, string_view directory,
650 string_view prefix, string_view extension)
652 if (argument.empty()) {
657 string filename(argument);
662 filename =
strCat(dir,
'/', filename);
667 if (!filename.ends_with(extension) && !
exists(filename)) {
672 filename.append(extension.data(), extension.size());
680 if (DWORD len = GetTempPathW(0,
nullptr)) {
681 std::wstring bufW(len,
L'\0');
682 if (
int len2 = GetTempPathW(len, bufW.data())) {
685 if (bufW.ends_with(
L'\\')) {
688 return utf16to8(bufW);
691 throw FatalError(
"GetTempPathW failed: ", GetLastError());
692#elif PLATFORM_ANDROID
696 const char* result =
nullptr;
697 if (!result) result = getenv(
"TMPDIR");
698 if (!result) result = getenv(
"TMP");
699 if (!result) result = getenv(
"TEMP");
710 std::wstring directoryW = utf8to16(directory);
711 std::array<wchar_t, MAX_PATH> filenameW;
712 if (!GetTempFileNameW(directoryW.c_str(),
L"msx", 0, filenameW.data())) {
713 throw FileException(
"GetTempFileNameW failed: ", GetLastError());
715 filename = utf16to8(filenameW.data());
716 return FILE_t(_wfopen(filenameW.data(),
L"wb"));
718 filename = directory +
"/XXXXXX";
719 auto oldMask = umask(S_IRWXO | S_IRWXG);
720 int fd = mkstemp(
const_cast<char*
>(filename.c_str()));
725 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
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 findResourceDir(const std::string &resourceDirName)
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.
string getNextNumberedFileName(string_view directory, string_view prefix, string_view extension, bool addSeparator)
Gets the next numbered file name with the specified prefix in the specified directory,...
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.
int deleteRecursive(const std::string &path)
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.
const string & getSystemDocDir()
Get system doc directory.
bool needsTildeExpansion(std::string_view path)
Returns true iff expandTilde(s) would have an effect.
const std::string & getConventionalPath(const std::string &path)
Returns the path in conventional path-delimiter.
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.
constexpr void replace(ForwardRange &&range, const T &old_value, const T &new_value)
octet_iterator utf16to8(u16bit_iterator start, u16bit_iterator end, octet_iterator result)