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());
116 int result = _wmkdir(utf8to16(
getNativePath(
string(path))).c_str());
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());
174 return ::unlink(path.
c_str());
181 return _wrmdir(utf8to16(path).c_str());
183 return ::rmdir(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* ,
208 return remove(fpath);
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);
247 return FILE_t(_wfopen(utf8to16(filename).c_str(),
248 utf8to16(mode).c_str()));
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)
348 std::array<wchar_t, MAXPATHLEN> bufW;
349 const wchar_t* result = _wgetcwd(bufW.data(), bufW.size());
351 throw FileException(
"Couldn't get current working directory.");
353 return utf16to8(result);
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] ==
'/');
393 std::array<wchar_t, MAXPATHLEN + 1> bufW;
394 if (!SHGetSpecialFolderPathW(
nullptr, bufW.data(), 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 const char* value = getenv(NAME);
450 static std::optional<string> result;
451 if (!result) result = []() ->
string {
452 if (
const char* value = getenv(
"OPENMSX_SYSTEM_DATA")) {
456 std::array<wchar_t, MAXPATHLEN + 1> bufW;
457 if (
int res = GetModuleFileNameW(
nullptr, bufW.data(), DWORD(bufW.size()));
460 "Cannot detect openMSX directory. GetModuleFileNameW failed: ",
464 string filename = utf16to8(bufW.data());
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
484 static std::optional<string> result;
485 if (!result) result = []() ->
string {
487 std::array<wchar_t, MAXPATHLEN + 1> bufW;
488 if (
int res = GetModuleFileNameW(
nullptr, bufW.data(), DWORD(bufW.size()));
491 "Cannot detect openMSX directory. GetModuleFileNameW failed: ",
495 string filename = utf16to8(bufW.data());
496 auto pos = filename.find_last_of(
'\\');
497 if (pos == string::npos) {
498 throw FatalError(
"openMSX is not in directory!?");
501#elif defined(__APPLE__)
503#elif PLATFORM_ANDROID
514static bool driveExists(
char driveLetter)
516 std::array<char, 3> buf = {driveLetter,
':', 0};
517 return GetFileAttributesA(buf.data()) != INVALID_FILE_ATTRIBUTES;
524 string result = path;
525 if (((path.size() == 2) && (path[1] ==
':')) ||
526 ((path.size() >= 3) && (path[1] ==
':') && (path[2] !=
'/'))) {
528 unsigned char drive = tolower(path[0]);
529 if ((
'a' <= drive) && (drive <=
'z')) {
530 std::array<wchar_t, MAXPATHLEN + 1> bufW;
531 if (driveExists(drive) &&
532 _wgetdcwd(drive -
'a' + 1, bufW.data(),
MAXPATHLEN)) {
534 if (result.back() !=
'/') {
537 if (path.size() > 2) {
538 auto tmp = std::string_view(path).substr(2);
539 result.append(tmp.data(), tmp.size());
550 std::optional<Stat> st;
554 std::string filename2(filename);
557 if (
auto pos = filename2.find_last_not_of(
'/'); pos != string::npos) {
558 filename2.resize(pos + 1);
561 if (!filename2.empty()) filename2.resize(1);
563 if (_wstat(utf8to16(filename2).c_str(), &*st)) {
567 if (stat(filename.
c_str(), &*st)) {
577 return S_ISREG(st.st_mode);
587 return S_ISDIR(st.st_mode);
598 return static_cast<bool>(
getStat(filename));
601static unsigned getNextNum(
const dirent* d, string_view prefix, string_view extension,
604 auto extensionLen = extension.size();
605 auto prefixLen = prefix.size();
606 string_view name(d->d_name);
608 if ((name.size() != (prefixLen + nofDigits + extensionLen)) ||
609 (!name.starts_with(prefix)) ||
610 (name.substr(prefixLen + nofDigits, extensionLen) != extension)) {
613 return StringOp::stringToBase<10, unsigned>(name.substr(prefixLen, nofDigits)).value_or(0);
617 string_view directory, string_view prefix, string_view extension,
bool addSeparator)
619 std::string newPrefix;
621 newPrefix =
strCat(prefix, ((prefix.find(
' ') != std::string_view::npos) ?
' ' :
'_'));
625 const unsigned nofDigits = 4;
627 unsigned max_num = 0;
638 max_num = std::max(max_num, getNextNum(d, prefix, extension, nofDigits));
641 std::ostringstream os;
645 os << (max_num + 1) << extension;
650 string_view argument, string_view directory,
651 string_view prefix, string_view extension)
653 if (argument.empty()) {
658 string filename(argument);
663 filename =
strCat(dir,
'/', filename);
668 if (!filename.ends_with(extension) && !
exists(filename)) {
673 filename.append(extension.data(), extension.size());
681 if (DWORD len = GetTempPathW(0,
nullptr)) {
682 VLA(
wchar_t, bufW, (len + 1));
683 len = GetTempPathW(len, bufW.data());
686 if (bufW[len - 1] ==
L'\\') {
687 bufW[len - 1] =
L'\0';
689 return utf16to8(bufW.data());
692 throw FatalError(
"GetTempPathW failed: ", GetLastError());
693#elif PLATFORM_ANDROID
697 const char* result =
nullptr;
698 if (!result) result = getenv(
"TMPDIR");
699 if (!result) result = getenv(
"TMP");
700 if (!result) result = getenv(
"TEMP");
711 std::wstring directoryW = utf8to16(directory);
712 std::array<wchar_t, MAX_PATH> filenameW;
713 if (!GetTempFileNameW(directoryW.c_str(),
L"msx", 0, filenameW.data())) {
714 throw FileException(
"GetTempFileNameW failed: ", GetLastError());
716 filename = utf16to8(filenameW.data());
717 return FILE_t(_wfopen(filenameW.data(),
L"wb"));
719 filename = directory +
"/XXXXXX";
720 auto oldMask = umask(S_IRWXO | S_IRWXG);
721 int fd = mkstemp(
const_cast<char*
>(filename.c_str()));
726 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)
#define VLA(TYPE, NAME, LENGTH)