openMSX
FileOperations.cc
Go to the documentation of this file.
1 #ifdef _WIN32
2 #ifndef _WIN32_IE
3 #define _WIN32_IE 0x0500 // For SHGetSpecialFolderPathW with MinGW
4 #endif
5 #include "utf8_checked.hh"
6 #include "vla.hh"
7 #include <windows.h>
8 #include <shlobj.h>
9 #include <shellapi.h>
10 #include <io.h>
11 #include <direct.h>
12 #include <ctype.h>
13 #include <cstdlib>
14 #include <cstring>
15 #include <algorithm>
16 #else // ifdef _WIN32_ ...
17 #include <sys/types.h>
18 #include <pwd.h>
19 #include <climits>
20 #include <unistd.h>
21 #endif // ifdef _WIN32_ ... else ...
22 
23 #include "openmsx.hh" // for ad_printf
24 
25 #include "systemfuncs.hh"
26 
27 #if HAVE_NFTW
28 #include <ftw.h>
29 #endif
30 
31 #if defined(PATH_MAX)
32 #define MAXPATHLEN PATH_MAX
33 #elif defined(MAX_PATH)
34 #define MAXPATHLEN MAX_PATH
35 #else
36 #define MAXPATHLEN 4096
37 #endif
38 
39 
40 #ifdef __APPLE__
41 #include <Carbon/Carbon.h>
42 #endif
43 
44 #include "ReadDir.hh"
45 #include "FileOperations.hh"
46 #include "FileException.hh"
47 #include "StringOp.hh"
48 #include "statp.hh"
49 #include "unistdp.hh"
50 #include "countof.hh"
51 #include "ranges.hh"
52 #include "strCat.hh"
53 #include "build-info.hh"
54 #include <algorithm>
55 #include <sstream>
56 #include <cerrno>
57 #include <cstdlib>
58 #include <stdexcept>
59 #include <cassert>
60 
61 #ifndef _MSC_VER
62 #include <dirent.h>
63 #endif
64 
65 #if PLATFORM_ANDROID
66 #include "SDL_system.h" // for GetExternalStorage stuff
67 #endif
68 
69 using std::string;
70 
71 #ifdef _WIN32
72 using namespace utf8;
73 #endif
74 
75 namespace openmsx {
76 
77 namespace FileOperations {
78 
79 #ifdef __APPLE__
80 
81 static std::string findShareDir()
82 {
83  // Find bundle location:
84  // for an app folder, this is the outer directory,
85  // for an unbundled executable, it is the executable itself.
86  ProcessSerialNumber psn;
87  if (GetCurrentProcess(&psn) != noErr) {
88  throw FatalError("Failed to get process serial number");
89  }
90  FSRef location;
91  if (GetProcessBundleLocation(&psn, &location) != noErr) {
92  throw FatalError("Failed to get process bundle location");
93  }
94  // Get info about the location.
95  FSCatalogInfo catalogInfo;
96  FSRef parentRef;
97  if (FSGetCatalogInfo(
98  &location, kFSCatInfoVolume | kFSCatInfoNodeFlags,
99  &catalogInfo, nullptr, nullptr, &parentRef
100  ) != noErr) {
101  throw FatalError("Failed to get info about bundle path");
102  }
103  // Get reference to root directory of the volume we are searching.
104  // We will need this later to know when to give up.
105  FSRef root;
106  if (FSGetVolumeInfo(
107  catalogInfo.volume, 0, nullptr, kFSVolInfoNone, nullptr, nullptr, &root
108  ) != noErr) {
109  throw FatalError("Failed to get reference to root directory");
110  }
111  // Make sure we are looking at a directory.
112  if (~catalogInfo.nodeFlags & kFSNodeIsDirectoryMask) {
113  // Location is not a directory, so it is the path to the executable.
114  location = parentRef;
115  }
116  while (true) {
117  // Iterate through the files in the directory.
118  FSIterator iterator;
119  if (FSOpenIterator(&location, kFSIterateFlat, &iterator) != noErr) {
120  throw FatalError("Failed to open iterator");
121  }
122  bool filesLeft = true; // iterator has files left for next call
123  while (filesLeft) {
124  // Get info about several files at a time.
125  const int MAX_SCANNED_FILES = 100;
126  ItemCount actualObjects;
127  FSRef refs[MAX_SCANNED_FILES];
128  FSCatalogInfo catalogInfos[MAX_SCANNED_FILES];
129  HFSUniStr255 names[MAX_SCANNED_FILES];
130  OSErr err = FSGetCatalogInfoBulk(
131  iterator,
132  MAX_SCANNED_FILES,
133  &actualObjects,
134  nullptr /*containerChanged*/,
135  kFSCatInfoNodeFlags,
136  catalogInfos,
137  refs,
138  nullptr /*specs*/,
139  names
140  );
141  if (err == errFSNoMoreItems) {
142  filesLeft = false;
143  } else if (err != noErr) {
144  throw FatalError("Catalog get failed");
145  }
146  for (ItemCount i = 0; i < actualObjects; i++) {
147  // We're only interested in subdirectories.
148  if (catalogInfos[i].nodeFlags & kFSNodeIsDirectoryMask) {
149  // Convert the name to a CFString.
150  CFStringRef name = CFStringCreateWithCharactersNoCopy(
151  kCFAllocatorDefault,
152  names[i].unicode,
153  names[i].length,
154  kCFAllocatorNull // do not deallocate character buffer
155  );
156  // Is this the directory we are looking for?
157  static const CFStringRef SHARE = CFSTR("share");
158  CFComparisonResult cmp = CFStringCompare(SHARE, name, 0);
159  CFRelease(name);
160  if (cmp == kCFCompareEqualTo) {
161  // Clean up.
162  OSErr closeErr = FSCloseIterator(iterator);
163  assert(closeErr == noErr); (void)closeErr;
164  // Get full path of directory.
165  UInt8 path[256];
166  if (FSRefMakePath(
167  &refs[i], path, sizeof(path)) != noErr
168  ) {
169  throw FatalError("Path too long");
170  }
171  return std::string(reinterpret_cast<char*>(path));
172  }
173  }
174  }
175  }
176  OSErr closeErr = FSCloseIterator(iterator);
177  assert(closeErr == noErr); (void)closeErr;
178  // Are we in the root yet?
179  if (FSCompareFSRefs(&location, &root) == noErr) {
180  throw FatalError("Could not find \"share\" directory anywhere");
181  }
182  // Go up one level.
183  if (FSGetCatalogInfo(
184  &location, kFSCatInfoNone, nullptr, nullptr, nullptr, &parentRef
185  ) != noErr
186  ) {
187  throw FatalError("Failed to get parent directory");
188  }
189  location = parentRef;
190  }
191 }
192 
193 #endif // __APPLE__
194 
196 {
197  if (path.empty() || path[0] != '~') {
198  return path.str();
199  }
200  auto pos = path.find_first_of('/');
201  string_view user = ((path.size() == 1) || (pos == 1))
202  ? string_view{}
203  : path.substr(1, (pos == string_view::npos) ? pos : pos - 1);
204  string result = getUserHomeDir(user);
205  if (result.empty()) {
206  // failed to find homedir, return the path unchanged
207  return path.str();
208  }
209  if (pos == string_view::npos) {
210  return result;
211  }
212  if (result.back() != '/') {
213  result += '/';
214  }
215  string_view last = path.substr(pos + 1);
216  result.append(last.data(), last.size());
217  return result;
218 }
219 
220 void mkdir(const string& path, mode_t mode)
221 {
222 #ifdef _WIN32
223  (void)&mode; // Suppress C4100 VC++ warning
224  if ((path == "/") ||
225  StringOp::endsWith(path, ':') ||
226  StringOp::endsWith(path, ":/")) {
227  return;
228  }
229  int result = _wmkdir(utf8to16(getNativePath(path)).c_str());
230 #else
231  int result = ::mkdir(path.c_str(), mode);
232 #endif
233  if (result && (errno != EEXIST)) {
234  throw FileException("Error creating dir ", path);
235  }
236 }
237 
238 static bool isUNCPath(string_view path)
239 {
240 #ifdef _WIN32
241  return path.starts_with("//") || path.starts_with("\\\\");
242 #else
243  (void)path;
244  return false;
245 #endif
246 }
247 
248 void mkdirp(string_view path_)
249 {
250  if (path_.empty()) {
251  return;
252  }
253 
254  // We may receive platform-specific paths here, so conventionalize early
255  string path = getConventionalPath(expandTilde(path_));
256 
257  // If the directory already exists, don't try to recreate it
258  if (isDirectory(path))
259  return;
260 
261  // If the path is a UNC path (e.g. \\server\share) then the first two paths in the loop below will be \ and \\server
262  // If the path is an absolute path (e.g. c:\foo\bar) then the first path in the loop will be C:
263  // None of those are valid directory paths, so we skip over them and don't call mkdir.
264  // Relative paths are fine, since each segment in the path is significant.
265  int skip = isUNCPath(path) ? 2 :
266  isAbsolutePath(path) ? 1 : 0;
267  string::size_type pos = 0;
268  do {
269  pos = path.find_first_of('/', pos + 1);
270  if (skip) {
271  skip--;
272  continue;
273  }
274  mkdir(path.substr(0, pos), 0755);
275  } while (pos != string::npos);
276 
277  if (!isDirectory(path)) {
278  throw FileException("Error creating dir ", path);
279  }
280 }
281 
282 int unlink(const std::string& path)
283 {
284 #ifdef _WIN32
285  return _wunlink(utf8to16(path).c_str());
286 #else
287  return ::unlink(path.c_str());
288 #endif
289 }
290 
291 int rmdir(const std::string& path)
292 {
293 #ifdef _WIN32
294  return _wrmdir(utf8to16(path).c_str());
295 #else
296  return ::rmdir(path.c_str());
297 #endif
298 }
299 
300 #ifdef _WIN32
301 int deleteRecursive(const std::string& path)
302 {
303  std::wstring pathW = utf8to16(path);
304 
305  SHFILEOPSTRUCTW rmdirFileop;
306  rmdirFileop.hwnd = nullptr;
307  rmdirFileop.wFunc = FO_DELETE;
308  rmdirFileop.pFrom = pathW.c_str();
309  rmdirFileop.pTo = nullptr;
310  rmdirFileop.fFlags = FOF_SILENT | FOF_NOCONFIRMATION | FOF_NOERRORUI;
311  rmdirFileop.fAnyOperationsAborted = FALSE;
312  rmdirFileop.hNameMappings = nullptr;
313  rmdirFileop.lpszProgressTitle = nullptr;
314 
315  return SHFileOperationW(&rmdirFileop);
316 }
317 #elif HAVE_NFTW
318 static int deleteRecursive_cb(const char* fpath, const struct stat* /*sb*/,
319  int /*typeflag*/, struct FTW* /*ftwbuf*/)
320 {
321  return remove(fpath);
322 }
323 int deleteRecursive(const std::string& path)
324 {
325  return nftw(path.c_str(), deleteRecursive_cb, 64, FTW_DEPTH | FTW_PHYS);
326 }
327 #else
328 // This is a platform independent version of deleteRecursive() (it builds on
329 // top of helper routines that _are_ platform specific). Though I still prefer
330 // the two platform specific deleteRecursive() routines above because they are
331 // likely more optimized and likely contain less bugs than this version (e.g.
332 // we're walking over the entries in a directory while simultaneously deleting
333 // entries in that same directory. Although this seems to work fine, I'm not
334 // 100% sure our ReadDir 'emulation code' for windows covers all corner cases.
335 // While the windows version above very likely does handle everything).
336 int deleteRecursive(const std::string& path)
337 {
338  if (isDirectory(path)) {
339  {
340  ReadDir dir(path);
341  while (dirent* d = dir.getEntry()) {
342  int err = deleteRecursive(d->d_name);
343  if (err) return err;
344  }
345  }
346  return rmdir(path);
347  } else {
348  return unlink(path);
349  }
350 }
351 #endif
352 
353 FILE_t openFile(const std::string& filename, const std::string& mode)
354 {
355  // Mode must contain a 'b' character. On unix this doesn't make any
356  // difference. But on windows this is required to open the file
357  // in binary mode.
358  assert(mode.find('b') != std::string::npos);
359 #ifdef _WIN32
360  return FILE_t(_wfopen(utf8to16(filename).c_str(),
361  utf8to16(mode).c_str()));
362 #else
363  return FILE_t(fopen(filename.c_str(), mode.c_str()));
364 #endif
365 }
366 
367 void openofstream(std::ofstream& stream, const std::string& filename)
368 {
369 #if defined _WIN32 && defined _MSC_VER
370  // MinGW 3.x doesn't support ofstream.open(wchar_t*)
371  // TODO - this means that unicode text may not work right here
372  stream.open(utf8to16(filename).c_str());
373 #else
374  stream.open(filename.c_str());
375 #endif
376 }
377 
378 void openofstream(std::ofstream& stream, const std::string& filename,
379  std::ios_base::openmode mode)
380 {
381 #if defined _WIN32 && defined _MSC_VER
382  // MinGW 3.x doesn't support ofstream.open(wchar_t*)
383  // TODO - this means that unicode text may not work right here
384  stream.open(utf8to16(filename).c_str(), mode);
385 #else
386  stream.open(filename.c_str(), mode);
387 #endif
388 }
389 
391 {
392  auto pos = path.rfind('/');
393  if (pos == string_view::npos) {
394  return path;
395  } else {
396  return path.substr(pos + 1);
397  }
398 }
399 
401 {
402  auto pos = path.rfind('/');
403  if (pos == string_view::npos) {
404  return {};
405  } else {
406  return path.substr(0, pos + 1);
407  }
408 }
409 
411 {
412  string_view filename = getFilename(path);
413  auto pos = filename.rfind('.');
414  if (pos == string_view::npos) {
415  return string_view();
416  } else {
417  return filename.substr(pos);
418  }
419 }
420 
422 {
423  auto pos = path.rfind('.');
424  if (pos == string_view::npos) {
425  return path;
426  } else {
427  return path.substr(0, pos);
428  }
429 }
430 
431 string join(string_view part1, string_view part2)
432 {
433  if (part1.empty() || isAbsolutePath(part2)) {
434  return part2.str();
435  }
436  if (part1.back() == '/') {
437  return strCat(part1, part2);
438  }
439  return strCat(part1, '/', part2);
440 }
441 string join(string_view part1, string_view part2, string_view part3)
442 {
443  return join(part1, join(part2, part3));
444 }
445 
446 string join(string_view part1, string_view part2,
447  string_view part3, string_view part4)
448 {
449  return join(part1, join(part2, join(part3, part4)));
450 }
451 
453 {
454  string result = path.str();
455 #ifdef _WIN32
456  ranges::replace(result, '/', '\\');
457 #endif
458  return result;
459 }
460 
462 {
463  string result = path.str();
464 #ifdef _WIN32
465  ranges::replace(result, '\\', '/');
466 #endif
467  return result;
468 }
469 
471 {
472 #ifdef _WIN32
473  wchar_t bufW[MAXPATHLEN];
474  wchar_t* result = _wgetcwd(bufW, MAXPATHLEN);
475  string buf;
476  if (result) {
477  buf = utf16to8(result);
478  }
479 #else
480  char buf[MAXPATHLEN];
481  char* result = getcwd(buf, MAXPATHLEN);
482 #endif
483  if (!result) {
484  throw FileException("Couldn't get current working directory.");
485  }
486  return buf;
487 }
488 
490 {
491  // In rare cases getCurrentWorkingDirectory() can throw,
492  // so only call it when really necessary.
493  if (isAbsolutePath(path)) {
494  return path.str();
495  }
496  string currentDir = getCurrentWorkingDirectory();
497  return join(currentDir, path);
498 }
499 
501 {
502  if (isUNCPath(path)) return true;
503 #ifdef _WIN32
504  if ((path.size() >= 3) && (((path[1] == ':') &&
505  ((path[2] == '/') || (path[2] == '\\'))))) {
506  char drive = tolower(path[0]);
507  if (('a' <= drive) && (drive <= 'z')) {
508  return true;
509  }
510  }
511 #endif
512  return !path.empty() && (path[0] == '/');
513 }
514 
515 string getUserHomeDir(string_view username)
516 {
517 #ifdef _WIN32
518  (void)(&username); // ignore parameter, avoid warning
519 
520  wchar_t bufW[MAXPATHLEN + 1];
521  if (!SHGetSpecialFolderPathW(nullptr, bufW, CSIDL_PERSONAL, TRUE)) {
522  throw FatalError(
523  "SHGetSpecialFolderPathW failed: ", GetLastError());
524  }
525 
526  return getConventionalPath(utf16to8(bufW));
527 #else
528  const char* dir = nullptr;
529  struct passwd* pw = nullptr;
530  if (username.empty()) {
531  dir = getenv("HOME");
532  if (!dir) {
533  pw = getpwuid(getuid());
534  }
535  } else {
536  pw = getpwnam(username.str().c_str());
537  }
538  if (pw) {
539  dir = pw->pw_dir;
540  }
541  return dir ? dir : string{};
542 #endif
543 }
544 
545 const string& getUserOpenMSXDir()
546 {
547 #ifdef _WIN32
548  static const string OPENMSX_DIR = expandTilde("~/openMSX");
549 #elif PLATFORM_ANDROID
550  // TODO: do something to query whether the storage is available
551  // via SDL_AndroidGetExternalStorageState
552  static const string OPENMSX_DIR = strCat(SDL_AndroidGetExternalStoragePath(), "/openMSX");
553 #else
554  static const string OPENMSX_DIR = expandTilde("~/.openMSX");
555 #endif
556  return OPENMSX_DIR;
557 }
558 
560 {
561  const char* const NAME = "OPENMSX_USER_DATA";
562  char* value = getenv(NAME);
563  return value ? value : getUserOpenMSXDir() + "/share";
564 }
565 
567 {
568  const char* const NAME = "OPENMSX_SYSTEM_DATA";
569  if (char* value = getenv(NAME)) {
570  return value;
571  }
572 
573  string newValue;
574 #ifdef _WIN32
575  wchar_t bufW[MAXPATHLEN + 1];
576  int res = GetModuleFileNameW(nullptr, bufW, countof(bufW));
577  if (!res) {
578  throw FatalError(
579  "Cannot detect openMSX directory. GetModuleFileNameW failed: ",
580  GetLastError());
581  }
582 
583  string filename = utf16to8(bufW);
584  auto pos = filename.find_last_of('\\');
585  if (pos == string::npos) {
586  throw FatalError("openMSX is not in directory!?");
587  }
588  newValue = getConventionalPath(filename.substr(0, pos)) + "/share";
589 #elif defined(__APPLE__)
590  newValue = findShareDir();
591 #elif PLATFORM_ANDROID
592  newValue = getAbsolutePath("openmsx_system");
593  ad_printf("System data dir: %s", newValue.c_str());
594 #else
595  // defined in build-info.hh (default /opt/openMSX/share)
596  newValue = DATADIR;
597 #endif
598  return newValue;
599 }
600 
601 #ifdef _WIN32
602 static bool driveExists(char driveLetter)
603 {
604  char buf[] = { driveLetter, ':', 0 };
605  return GetFileAttributesA(buf) != INVALID_FILE_ATTRIBUTES;
606 }
607 #endif
608 
610 {
611  string result = path.str();
612 #ifdef _WIN32
613  if (((path.size() == 2) && (path[1] == ':')) ||
614  ((path.size() >= 3) && (path[1] == ':') && (path[2] != '/'))) {
615  // get current directory for this drive
616  unsigned char drive = tolower(path[0]);
617  if (('a' <= drive) && (drive <= 'z')) {
618  wchar_t bufW[MAXPATHLEN + 1];
619  if (driveExists(drive) &&
620  _wgetdcwd(drive - 'a' + 1, bufW, MAXPATHLEN)) {
621  result = getConventionalPath(utf16to8(bufW));
622  if (result.back() != '/') {
623  result += '/';
624  }
625  if (path.size() > 2) {
626  string_view tmp = path.substr(2);
627  result.append(tmp.data(), tmp.size());
628  }
629  }
630  }
631  }
632 #endif
633  return result;
634 }
635 
636 bool getStat(string_view filename_, Stat& st)
637 {
638  string filename = expandTilde(filename_);
639  // workaround for VC++: strip trailing slashes (but keep it if it's the
640  // only character in the path)
641  auto pos = filename.find_last_not_of('/');
642  if (pos == string::npos) {
643  // string was either empty or a (sequence of) '/' character(s)
644  filename = filename.empty() ? string{} : "/";
645  } else {
646  filename.resize(pos + 1);
647  }
648 #ifdef _WIN32
649  return _wstat(utf8to16(filename).c_str(), &st) == 0;
650 #else
651  return stat(filename.c_str(), &st) == 0;
652 #endif
653 }
654 
655 bool isRegularFile(const Stat& st)
656 {
657  return S_ISREG(st.st_mode);
658 }
660 {
661  Stat st;
662  return getStat(filename, st) && isRegularFile(st);
663 }
664 
665 bool isDirectory(const Stat& st)
666 {
667  return S_ISDIR(st.st_mode);
668 }
669 
670 bool isDirectory(string_view directory)
671 {
672  Stat st;
673  return getStat(directory, st) && isDirectory(st);
674 }
675 
676 bool exists(string_view filename)
677 {
678  Stat st; // dummy
679  return getStat(filename, st);
680 }
681 
682 time_t getModificationDate(const Stat& st)
683 {
684  return st.st_mtime;
685 }
686 
687 static unsigned getNextNum(dirent* d, string_view prefix, string_view extension,
688  unsigned nofdigits)
689 {
690  auto extensionLen = extension.size();
691  auto prefixLen = prefix.size();
692  string_view name(d->d_name);
693 
694  if ((name.size() != (prefixLen + nofdigits + extensionLen)) ||
695  (name.substr(0, prefixLen) != prefix) ||
696  (name.substr(prefixLen + nofdigits, extensionLen) != extension)) {
697  return 0;
698  }
699  try {
700  return fast_stou(name.substr(prefixLen, nofdigits));
701  } catch (std::invalid_argument&) {
702  return 0;
703  }
704 }
705 
707  string_view directory, string_view prefix, string_view extension)
708 {
709  const unsigned nofdigits = 4;
710 
711  unsigned max_num = 0;
712 
713  string dirName = strCat(getUserOpenMSXDir(), '/', directory);
714  try {
715  mkdirp(dirName);
716  } catch (FileException&) {
717  // ignore
718  }
719 
720  ReadDir dir(dirName);
721  while (auto* d = dir.getEntry()) {
722  max_num = std::max(max_num, getNextNum(d, prefix, extension, nofdigits));
723  }
724 
725  std::ostringstream os;
726  os << dirName << '/' << prefix;
727  os.width(nofdigits);
728  os.fill('0');
729  os << (max_num + 1) << extension;
730  return os.str();
731 }
732 
734  string_view argument, string_view directory,
735  string_view prefix, string_view extension)
736 {
737  if (argument.empty()) {
738  // directory is also created when needed
739  return getNextNumberedFileName(directory, prefix, extension);
740  }
741 
742  string filename = argument.str();
743  if (getDirName(filename).empty()) {
744  // no dir given, use standard dir (and create it)
745  string dir = strCat(getUserOpenMSXDir(), '/', directory);
746  mkdirp(dir);
747  filename = strCat(dir, '/', filename);
748  } else {
749  filename = expandTilde(filename);
750  }
751 
752  if (!StringOp::endsWith(filename, extension) &&
753  !exists(filename)) {
754  // Expected extension not already given, append it. But only
755  // when the filename without extension doesn't already exist.
756  // Without this exception stuff like 'soundlog start /dev/null'
757  // reports an error " ... error opening file /dev/null.wav."
758  filename.append(extension.data(), extension.size());
759  }
760  return filename;
761 }
762 
763 string getTempDir()
764 {
765 #ifdef _WIN32
766  DWORD len = GetTempPathW(0, nullptr);
767  if (len) {
768  VLA(wchar_t, bufW, (len+1));
769  len = GetTempPathW(len, bufW);
770  if (len) {
771  // Strip last backslash
772  if (bufW[len-1] == L'\\') {
773  bufW[len-1] = L'\0';
774  }
775  return utf16to8(bufW);
776  }
777  }
778  throw FatalError("GetTempPathW failed: ", GetLastError());
779 #elif PLATFORM_ANDROID
780  string result = getSystemDataDir() + "/tmp";
781  return result;
782 #else
783  const char* result = nullptr;
784  if (!result) result = getenv("TMPDIR");
785  if (!result) result = getenv("TMP");
786  if (!result) result = getenv("TEMP");
787  if (!result) {
788  result = "/tmp";
789  }
790  return result;
791 #endif
792 }
793 
794 FILE_t openUniqueFile(const std::string& directory, std::string& filename)
795 {
796 #ifdef _WIN32
797  std::wstring directoryW = utf8to16(directory);
798  wchar_t filenameW[MAX_PATH];
799  if (!GetTempFileNameW(directoryW.c_str(), L"msx", 0, filenameW)) {
800  throw FileException("GetTempFileNameW failed: ", GetLastError());
801  }
802  filename = utf16to8(filenameW);
803  return FILE_t(_wfopen(filenameW, L"wb"));
804 #else
805  filename = directory + "/XXXXXX";
806  auto oldMask = umask(S_IRWXO | S_IRWXG);
807  int fd = mkstemp(const_cast<char*>(filename.c_str()));
808  umask(oldMask);
809  if (fd == -1) {
810  throw FileException("Coundn't get temp file name");
811  }
812  return FILE_t(fdopen(fd, "wb"));
813 #endif
814 }
815 
816 } // namespace FileOperations
817 
818 } // namespace openmsx
string_view getFilename(string_view path)
Returns the file portion of a path name.
const char * data() const
Definition: string_view.hh:57
T length(const vecN< N, T > &x)
Definition: gl_vec.hh:343
bool isAbsolutePath(string_view path)
Checks whether it&#39;s a absolute path or not.
bool starts_with(string_view x) const
Definition: string_view.cc:116
int unlink(const std::string &path)
Call unlink() in a platform-independent manner.
size_type find_first_of(string_view s) const
Definition: string_view.cc:87
string parseCommandFileArgument(string_view argument, string_view directory, string_view prefix, string_view extension)
Helper function for parsing filename arguments in Tcl commands.
void replace(ForwardRange &&range, const T &old_value, const T &new_value)
Definition: ranges.hh:179
bool isRegularFile(string_view filename)
Is this a regular file (no directory, device, ..)?
string getSystemDataDir()
Get system directory.
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, with the specified extension.
string getCurrentWorkingDirectory()
Returns the current working directory.
char back() const
Definition: string_view.hh:56
vecN< N, T > max(const vecN< N, T > &x, const vecN< N, T > &y)
Definition: gl_vec.hh:287
bool isDirectory(string_view directory)
Is this a directory?
void openofstream(std::ofstream &stream, const std::string &filename, std::ios_base::openmode mode)
Open an ofstream in a platform-independent manner.
octet_iterator utf16to8(u16bit_iterator start, u16bit_iterator end, octet_iterator result)
static const size_type npos
Definition: string_view.hh:24
FILE_t openUniqueFile(const std::string &directory, std::string &filename)
Open a new file with a unique name in the provided directory.
#define MAXPATHLEN
string expandCurrentDirFromDrive(string_view path)
Get the current directory of the specified drive Linux: just return an empty string.
string getConventionalPath(string_view path)
Returns the path in conventional path-delimiter.
string getNativePath(string_view path)
Returns the path in native path-delimiter.
string getUserDataDir()
Get the openMSX data dir in the user&#39;s home directory.
Thanks to enen for testing this on a real cartridge:
Definition: Autofire.cc:5
bool getStat(string_view filename_, Stat &st)
Call stat() and return the stat structure.
string getAbsolutePath(string_view path)
Transform given path into an absolute path.
bool exists(string_view filename)
Does this file (directory) exists?
size_type rfind(string_view s) const
Definition: string_view.cc:65
int rmdir(const std::string &path)
Call rmdir() in a platform-independent manner.
string_view getExtension(string_view path)
Returns the extension portion of a path.
#define countof(array)
Definition: countof.hh:48
string getUserHomeDir(string_view username)
Get user&#39;s home directory.
bool empty() const
Definition: string_view.hh:45
This class implements a (close approximation) of the std::string_view class.
Definition: string_view.hh:16
const string & getUserOpenMSXDir()
Get the openMSX dir in the user&#39;s home directory.
void mkdir(const string &path, mode_t mode)
Create the specified directory.
string_view stripExtension(string_view path)
Returns the path without extension.
std::string str() const
Definition: string_view.cc:12
u16bit_iterator utf8to16(octet_iterator start, octet_iterator end, u16bit_iterator result)
string_view getDirName(string_view path)
Returns the directory portion of a path.
string getTempDir()
Get the name of the temp directory on the system.
string_view substr(size_type pos, size_type n=npos) const
Definition: string_view.cc:32
Simple wrapper around openmdir() / readdir() / closedir() functions.
Definition: ReadDir.hh:15
void mkdirp(string_view path_)
Acts like the unix command "mkdir -p".
FILE_t openFile(const std::string &filename, const std::string &mode)
Call fopen() in a platform-independent manner.
std::string strCat(Ts &&...ts)
Definition: strCat.hh:577
size_type size() const
Definition: string_view.hh:44
struct dirent * getEntry()
Get directory entry for next file.
Definition: ReadDir.cc:17
#define VLA(TYPE, NAME, LENGTH)
Definition: vla.hh:10
bool endsWith(string_view total, string_view part)
Definition: StringOp.cc:78
unsigned fast_stou(string_view s)
Definition: string_view.cc:145
detail::Joiner< Collection, Separator > join(Collection &&col, Separator &&sep)
Definition: join.hh:60
string expandTilde(string_view path)
Expand the &#39;~&#39; character to the users home directory.
std::unique_ptr< FILE, FClose > FILE_t
int deleteRecursive(const std::string &path)
Recurively delete a file or directory and (in case of a directory) all its sub-components.
time_t getModificationDate(const Stat &st)
Get the date/time of last modification.
#define ad_printf(...)
Definition: openmsx.hh:11