3 #define _WIN32_IE 0x0500
17 #include <sys/types.h>
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());
120 if (result && (errno != EEXIST)) {
125 static 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);
205 static 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
266 std::ios_base::openmode mode)
268 #if defined _WIN32 && defined _MSC_VER
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) {
304 if (
auto pos = path.rfind(
'.'); pos != string_view::npos) {
305 return path.substr(0, pos);
310 string 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);
320 string join(string_view part1, string_view part2, string_view part3)
322 return join(part1,
join(part2, part3));
325 string join(string_view part1, string_view part2,
326 string_view part3, string_view part4)
359 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
483 static 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());
523 if (
auto pos = filename2.find_last_not_of(
'/'); pos != string::npos) {
524 filename2.resize(pos + 1);
527 if (!filename2.empty()) filename2.resize(1);
529 return _wstat(
utf8to16(filename2).c_str(), &st) == 0;
531 return stat(
filename.c_str(), &st) == 0;
537 return S_ISREG(st.st_mode);
547 return S_ISDIR(st.st_mode);
562 static unsigned getNextNum(dirent* d, string_view prefix, string_view extension,
565 auto extensionLen = extension.size();
566 auto prefixLen = prefix.size();
567 string_view name(d->d_name);
569 if ((name.size() != (prefixLen + nofdigits + extensionLen)) ||
570 (name.substr(0, prefixLen) != prefix) ||
571 (name.substr(prefixLen + nofdigits, extensionLen) != extension)) {
574 if (
auto n = StringOp::stringToBase<10, unsigned>(name.substr(prefixLen, nofdigits))) {
581 string_view directory, string_view prefix, string_view extension)
583 const unsigned nofdigits = 4;
585 unsigned max_num = 0;
596 max_num =
std::max(max_num, getNextNum(d, prefix, extension, nofdigits));
599 std::ostringstream os;
600 os << dirName <<
'/' << prefix;
603 os << (max_num + 1) << extension;
608 string_view argument, string_view directory,
609 string_view prefix, string_view extension)
611 if (argument.empty()) {
631 filename.append(extension.data(), extension.size());
639 DWORD len = GetTempPathW(0,
nullptr);
641 VLA(
wchar_t, bufW, (len+1));
642 len = GetTempPathW(len, bufW);
645 if (bufW[len-1] ==
L'\\') {
651 throw FatalError(
"GetTempPathW failed: ", GetLastError());
652 #elif PLATFORM_ANDROID
656 const char* result =
nullptr;
657 if (!result) result = getenv(
"TMPDIR");
658 if (!result) result = getenv(
"TMP");
659 if (!result) result = getenv(
"TEMP");
670 std::wstring directoryW =
utf8to16(directory);
671 wchar_t filenameW[MAX_PATH];
672 if (!GetTempFileNameW(directoryW.c_str(),
L"msx", 0, filenameW)) {
673 throw FileException(
"GetTempFileNameW failed: ", GetLastError());
676 return FILE_t(_wfopen(filenameW,
L"wb"));
679 auto oldMask = umask(S_IRWXO | S_IRWXG);
680 int fd = mkstemp(
const_cast<char*
>(
filename.c_str()));
685 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 const char * c_str() const
constexpr bool ends_with(std::string_view sv) const
constexpr auto find(char c, size_type pos=0) const
detail::Joiner< Collection, Separator > join(Collection &&col, Separator &&sep)
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.
bool isDirectory(zstring_view directory)
Is this a 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 getCurrentWorkingDirectory()
Returns the current working directory.
bool getStat(zstring_view filename, Stat &st)
Call stat() and return the stat structure.
string getUserHomeDir(string_view username)
Get user's home directory.
const std::string & getConventionalPath(const std::string &path)
Returns the path in conventional path-delimiter.
string_view getExtension(string_view path)
Returns the extension portion of a path.
void openofstream(std::ofstream &stream, zstring_view filename, std::ios_base::openmode mode)
Open an ofstream in a platform-independent manner.
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.
const std::string & getNativePath(const std::string &path)
Returns the path in native path-delimiter.
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.
bool isRegularFile(zstring_view filename)
Is this a regular file (no directory, device, ..)?
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.
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.
const std::string & expandCurrentDirFromDrive(const std::string &path)
Get the current directory of the specified drive Linux: return the given string unchanged.
constexpr const char *const filename
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)