3 #define _WIN32_IE 0x0500 // For SHGetSpecialFolderPathW with MinGW
16 #else // ifdef _WIN32_ ...
17 #include <sys/types.h>
21 #endif // ifdef _WIN32_ ... else ...
30 #define MAXPATHLEN PATH_MAX
31 #elif defined(MAX_PATH)
32 #define MAXPATHLEN MAX_PATH
34 #define MAXPATHLEN 4096
39 #include <Carbon/Carbon.h>
69 namespace FileOperations {
73 static std::string findShareDir()
78 ProcessSerialNumber psn;
79 if (GetCurrentProcess(&psn) != noErr) {
80 throw FatalError(
"Failed to get process serial number");
83 if (GetProcessBundleLocation(&psn, &location) != noErr) {
84 throw FatalError(
"Failed to get process bundle location");
87 FSCatalogInfo catalogInfo;
90 &location, kFSCatInfoVolume | kFSCatInfoNodeFlags,
91 &catalogInfo,
nullptr,
nullptr, &parentRef
93 throw FatalError(
"Failed to get info about bundle path");
99 catalogInfo.volume, 0,
nullptr, kFSVolInfoNone,
nullptr,
nullptr, &root
101 throw FatalError(
"Failed to get reference to root directory");
104 if (~catalogInfo.nodeFlags & kFSNodeIsDirectoryMask) {
106 location = parentRef;
111 if (FSOpenIterator(&location, kFSIterateFlat, &iterator) != noErr) {
114 bool filesLeft =
true;
117 const int MAX_SCANNED_FILES = 100;
118 ItemCount actualObjects;
119 FSRef refs[MAX_SCANNED_FILES];
120 FSCatalogInfo catalogInfos[MAX_SCANNED_FILES];
121 HFSUniStr255 names[MAX_SCANNED_FILES];
122 OSErr err = FSGetCatalogInfoBulk(
133 if (err == errFSNoMoreItems) {
135 }
else if (err != noErr) {
138 for (ItemCount i = 0; i < actualObjects; i++) {
140 if (catalogInfos[i].nodeFlags & kFSNodeIsDirectoryMask) {
142 CFStringRef name = CFStringCreateWithCharactersNoCopy(
149 static const CFStringRef SHARE = CFSTR(
"share");
150 CFComparisonResult cmp = CFStringCompare(SHARE, name, 0);
152 if (cmp == kCFCompareEqualTo) {
154 OSErr closeErr = FSCloseIterator(iterator);
155 assert(closeErr == noErr); (void)closeErr;
159 &refs[i], path,
sizeof(path)) != noErr
163 return std::string(reinterpret_cast<char*>(path));
168 OSErr closeErr = FSCloseIterator(iterator);
169 assert(closeErr == noErr); (void)closeErr;
171 if (FSCompareFSRefs(&location, &root) == noErr) {
172 throw FatalError(
"Could not find \"share\" directory anywhere");
175 if (FSGetCatalogInfo(
176 &location, kFSCatInfoNone,
nullptr,
nullptr,
nullptr, &parentRef
179 throw FatalError(
"Failed to get parent directory");
181 location = parentRef;
189 if (path.
empty() || path[0] !=
'~') {
197 if (result.empty()) {
204 if (result.back() !=
'/') {
208 result.append(last.
data(), last.
size());
212 void mkdir(
const string& path, mode_t mode)
223 int result =
::mkdir(path.c_str(), mode);
225 if (result && (errno != EEXIST)) {
237 string::size_type pos = 0;
239 pos = path.find_first_of(
'/', pos + 1);
240 mkdir(path.substr(0, pos), 0755);
241 }
while (pos != string::npos);
251 return _wunlink(
utf8to16(path).c_str());
260 return _wrmdir(
utf8to16(path).c_str());
269 std::wstring pathW =
utf8to16(path);
271 SHFILEOPSTRUCTW rmdirFileop;
272 rmdirFileop.hwnd =
nullptr;
273 rmdirFileop.wFunc = FO_DELETE;
274 rmdirFileop.pFrom = pathW.c_str();
275 rmdirFileop.pTo =
nullptr;
276 rmdirFileop.fFlags = FOF_SILENT | FOF_NOCONFIRMATION | FOF_NOERRORUI;
277 rmdirFileop.fAnyOperationsAborted = FALSE;
278 rmdirFileop.hNameMappings =
nullptr;
279 rmdirFileop.lpszProgressTitle =
nullptr;
281 return SHFileOperationW(&rmdirFileop);
287 return remove(fpath);
307 while (dirent* d = dir.getEntry()) {
319 FILE*
openFile(
const std::string& filename,
const std::string& mode)
324 assert(mode.find(
'b') != std::string::npos);
330 return fopen(filename.c_str(), mode.c_str());
336 #if defined _WIN32 && defined _MSC_VER
339 stream.open(
utf8to16(filename).c_str());
341 stream.open(filename.c_str());
346 std::ios_base::openmode mode)
348 #if defined _WIN32 && defined _MSC_VER
351 stream.open(
utf8to16(filename).c_str(), mode);
353 stream.open(filename.c_str(), mode);
359 auto pos = path.
rfind(
'/');
363 return path.
substr(pos + 1);
369 auto pos = path.
rfind(
'/');
373 return path.
substr(0, pos + 1);
380 auto pos = filename.
rfind(
'.');
384 return filename.
substr(pos + 1);
390 auto pos = path.
rfind(
'.');
394 return path.
substr(0, pos);
403 if (part1.
back() ==
'/') {
404 return part1 + part2;
406 return part1 +
'/' + part2;
410 return join(part1,
join(part2, part3));
421 string result = path.
str();
423 replace(result.begin(), result.end(),
'/',
'\\');
430 string result = path.
str();
432 replace(result.begin(), result.end(),
'\\',
'/');
451 throw FileException(
"Couldn't get current working directory.");
464 return join(currentDir, path);
470 if ((path.
size() >= 3) && (path[1] ==
':') && (path[2] ==
'/')) {
471 char drive = tolower(path[0]);
472 if ((
'a' <= drive) && (drive <=
'z')) {
477 return !path.
empty() && (path[0] ==
'/');
486 if (!SHGetSpecialFolderPathW(
nullptr, bufW, CSIDL_PERSONAL, TRUE)) {
488 "SHGetSpecialFolderPathW failed: " << GetLastError());
498 const char* dir = getenv(
"HOME");
504 const char* dir =
nullptr;
505 struct passwd* pw =
nullptr;
506 if (username.
empty()) {
507 dir = getenv(
"HOME");
509 pw = getpwuid(getuid());
512 pw = getpwnam(username.
str().c_str());
517 return dir ? dir :
"";
524 static const string OPENMSX_DIR =
expandTilde(
"~/openMSX");
525 #elif PLATFORM_ANDROID
526 static const string OPENMSX_DIR = AndroidApiWrapper::getStorageDirectory() +
"/openMSX";
528 static const string OPENMSX_DIR =
expandTilde(
"~/.openMSX");
535 const char*
const NAME =
"OPENMSX_USER_DATA";
536 char* value = getenv(NAME);
542 const char*
const NAME =
"OPENMSX_SYSTEM_DATA";
543 if (
char* value = getenv(NAME)) {
550 int res = GetModuleFileNameW(
nullptr, bufW,
countof(bufW));
553 "Cannot detect openMSX directory. GetModuleFileNameW failed: " <<
558 auto pos = filename.find_last_of(
'\\');
559 if (pos == string::npos) {
560 throw FatalError(
"openMSX is not in directory!?");
563 #elif defined(__APPLE__)
564 newValue = findShareDir();
565 #elif PLATFORM_ANDROID
567 ad_printf(
"System data dir: %s", newValue.c_str());
576 bool driveExists(
char driveLetter)
578 char buf[] = { driveLetter,
':', 0 };
579 return GetFileAttributesA(buf) != INVALID_FILE_ATTRIBUTES;
585 string result = path.
str();
587 if (((path.
size() == 2) && (path[1] ==
':')) ||
588 ((path.
size() >= 3) && (path[1] ==
':') && (path[2] !=
'/'))) {
590 unsigned char drive = tolower(path[0]);
591 if ((
'a' <= drive) && (drive <=
'z')) {
593 if (driveExists(drive) &&
594 _wgetdcwd(drive -
'a' + 1, bufW,
MAXPATHLEN)) {
596 if (result.back() !=
'/') {
599 if (path.
size() > 2) {
601 result.append(tmp.
data(), tmp.
size());
615 auto pos = filename.find_last_not_of(
'/');
616 if (pos == string::npos) {
618 filename = filename.empty() ?
"" :
"/";
620 filename.resize(pos + 1);
623 return _wstat(
utf8to16(filename).c_str(), &st) == 0;
625 return stat(filename.c_str(), &st) == 0;
631 return S_ISREG(st.st_mode);
641 return S_ISDIR(st.st_mode);
664 auto extensionLen = extension.
size();
665 auto prefixLen = prefix.
size();
668 if ((name.size() != (prefixLen + nofdigits + extensionLen)) ||
669 (name.substr(0, prefixLen) != prefix) ||
670 (name.substr(prefixLen + nofdigits, extensionLen) != extension)) {
675 unsigned long n =
stoul(num, &idx, 10);
676 return (idx == num.
size()) ? n : 0;
682 const unsigned nofdigits = 4;
694 while (dirent* d = dir.
getEntry()) {
695 max_num = std::max(max_num, getNextNum(d, prefix, extension, nofdigits));
698 std::ostringstream os;
699 os << dirName <<
'/' << prefix;
702 os << (max_num + 1) << extension;
710 if (argument.
empty()) {
715 string filename = argument.
str();
720 filename = dir +
'/' + filename;
731 filename.append(extension.
data(), extension.
size());
739 DWORD len = GetTempPathW(0,
nullptr);
741 VLA(
wchar_t, bufW, (len+1));
742 len = GetTempPathW(len, bufW);
745 if (bufW[len-1] == L
'\\') {
752 "GetTempPathW failed: " << GetLastError());
753 #elif PLATFORM_ANDROID
757 const char* result =
nullptr;
758 if (!result) result = getenv(
"TMPDIR");
759 if (!result) result = getenv(
"TMP");
760 if (!result) result = getenv(
"TEMP");
771 std::wstring directoryW =
utf8to16(directory);
772 wchar_t filenameW[MAX_PATH];
773 if (!GetTempFileNameW(directoryW.c_str(), L
"msx", 0, filenameW)) {
775 "GetTempFileNameW failed: " << GetLastError());
778 FILE* fp = _wfopen(filenameW, L
"wb");
780 filename = directory +
"/XXXXXX";
781 int fd = mkstemp(const_cast<char*>(filename.c_str()));
785 FILE* fp = fdopen(fd,
"wb");