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 "one_of.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 #include <iterator>
61 
62 #ifndef _MSC_VER
63 #include <dirent.h>
64 #endif
65 
66 #if PLATFORM_ANDROID
67 #include "SDL_system.h" // for GetExternalStorage stuff
68 #endif
69 
70 using std::string;
71 using std::string_view;
72 
73 #ifdef _WIN32
74 using namespace utf8;
75 #endif
76 
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 
195 string expandTilde(string_view path)
196 {
197  if (path.empty() || path[0] != '~') {
198  return string(path);
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 string(path);
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 StringOp::startsWith(path, "//") || StringOp::startsWith(path, "\\\\");
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 
390 string_view getFilename(string_view path)
391 {
392  if (auto pos = path.rfind('/'); pos != string_view::npos) {
393  return path.substr(pos + 1);
394  }
395  return path;
396 }
397 
398 string_view getDirName(string_view path)
399 {
400  if (auto pos = path.rfind('/'); pos != string_view::npos) {
401  return path.substr(0, pos + 1);
402  }
403  return {};
404 }
405 
406 string_view getExtension(string_view path)
407 {
408  string_view filename = getFilename(path);
409  if (auto pos = filename.rfind('.'); pos != string_view::npos) {
410  return filename.substr(pos);
411  }
412  return {};
413 }
414 
415 string_view stripExtension(string_view path)
416 {
417  if (auto pos = path.rfind('.'); pos != string_view::npos) {
418  return path.substr(0, pos);
419  }
420  return path;
421 }
422 
423 string join(string_view part1, string_view part2)
424 {
425  if (part1.empty() || isAbsolutePath(part2)) {
426  return string(part2);
427  }
428  if (part1.back() == '/') {
429  return strCat(part1, part2);
430  }
431  return strCat(part1, '/', part2);
432 }
433 string join(string_view part1, string_view part2, string_view part3)
434 {
435  return join(part1, join(part2, part3));
436 }
437 
438 string join(string_view part1, string_view part2,
439  string_view part3, string_view part4)
440 {
441  return join(part1, join(part2, join(part3, part4)));
442 }
443 
444 string getNativePath(string_view path)
445 {
446  string result(path);
447 #ifdef _WIN32
448  ranges::replace(result, '/', '\\');
449 #endif
450  return result;
451 }
452 
453 #ifdef _WIN32
454 string getConventionalPath(string path)
455 {
456  ranges::replace(path, '\\', '/');
457  return path;
458 }
459 #endif
460 
462 {
463 #ifdef _WIN32
464  wchar_t bufW[MAXPATHLEN];
465  wchar_t* result = _wgetcwd(bufW, MAXPATHLEN);
466  string buf;
467  if (result) {
468  buf = utf16to8(result);
469  }
470 #else
471  char buf[MAXPATHLEN];
472  char* result = getcwd(buf, MAXPATHLEN);
473 #endif
474  if (!result) {
475  throw FileException("Couldn't get current working directory.");
476  }
477  return buf;
478 }
479 
480 string getAbsolutePath(string_view path)
481 {
482  // In rare cases getCurrentWorkingDirectory() can throw,
483  // so only call it when really necessary.
484  if (isAbsolutePath(path)) {
485  return string(path);
486  }
487  string currentDir = getCurrentWorkingDirectory();
488  return join(currentDir, path);
489 }
490 
491 bool isAbsolutePath(string_view path)
492 {
493  if (isUNCPath(path)) return true;
494 #ifdef _WIN32
495  if ((path.size() >= 3) && (path[1] == ':') && (path[2] == one_of('/', '\\'))) {
496  char drive = tolower(path[0]);
497  if (('a' <= drive) && (drive <= 'z')) {
498  return true;
499  }
500  }
501 #endif
502  return !path.empty() && (path[0] == '/');
503 }
504 
505 string getUserHomeDir(string_view username)
506 {
507 #ifdef _WIN32
508  (void)(&username); // ignore parameter, avoid warning
509 
510  wchar_t bufW[MAXPATHLEN + 1];
511  if (!SHGetSpecialFolderPathW(nullptr, bufW, CSIDL_PERSONAL, TRUE)) {
512  throw FatalError(
513  "SHGetSpecialFolderPathW failed: ", GetLastError());
514  }
515 
516  return getConventionalPath(utf16to8(bufW));
517 #else
518  const char* dir = nullptr;
519  struct passwd* pw = nullptr;
520  if (username.empty()) {
521  dir = getenv("HOME");
522  if (!dir) {
523  pw = getpwuid(getuid());
524  }
525  } else {
526  pw = getpwnam(string(username).c_str());
527  }
528  if (pw) {
529  dir = pw->pw_dir;
530  }
531  return dir ? dir : string{};
532 #endif
533 }
534 
535 const string& getUserOpenMSXDir()
536 {
537 #ifdef _WIN32
538  static const string OPENMSX_DIR = expandTilde("~/openMSX");
539 #elif PLATFORM_ANDROID
540  // TODO: do something to query whether the storage is available
541  // via SDL_AndroidGetExternalStorageState
542  static const string OPENMSX_DIR = strCat(SDL_AndroidGetExternalStoragePath(), "/openMSX");
543 #else
544  static const string OPENMSX_DIR = expandTilde("~/.openMSX");
545 #endif
546  return OPENMSX_DIR;
547 }
548 
550 {
551  const char* const NAME = "OPENMSX_USER_DATA";
552  char* value = getenv(NAME);
553  return value ? value : getUserOpenMSXDir() + "/share";
554 }
555 
557 {
558  const char* const NAME = "OPENMSX_SYSTEM_DATA";
559  if (char* value = getenv(NAME)) {
560  return value;
561  }
562 
563  string newValue;
564 #ifdef _WIN32
565  wchar_t bufW[MAXPATHLEN + 1];
566  int res = GetModuleFileNameW(nullptr, bufW, std::size(bufW));
567  if (!res) {
568  throw FatalError(
569  "Cannot detect openMSX directory. GetModuleFileNameW failed: ",
570  GetLastError());
571  }
572 
573  string filename = utf16to8(bufW);
574  auto pos = filename.find_last_of('\\');
575  if (pos == string::npos) {
576  throw FatalError("openMSX is not in directory!?");
577  }
578  newValue = getConventionalPath(filename.substr(0, pos)) + "/share";
579 #elif defined(__APPLE__)
580  newValue = findShareDir();
581 #elif PLATFORM_ANDROID
582  newValue = getAbsolutePath("openmsx_system");
583  ad_printf("System data dir: %s", newValue.c_str());
584 #else
585  // defined in build-info.hh (default /opt/openMSX/share)
586  newValue = DATADIR;
587 #endif
588  return newValue;
589 }
590 
591 #ifdef _WIN32
592 static bool driveExists(char driveLetter)
593 {
594  char buf[] = { driveLetter, ':', 0 };
595  return GetFileAttributesA(buf) != INVALID_FILE_ATTRIBUTES;
596 }
597 #endif
598 
599 string expandCurrentDirFromDrive(string_view path)
600 {
601  string result(path);
602 #ifdef _WIN32
603  if (((path.size() == 2) && (path[1] == ':')) ||
604  ((path.size() >= 3) && (path[1] == ':') && (path[2] != '/'))) {
605  // get current directory for this drive
606  unsigned char drive = tolower(path[0]);
607  if (('a' <= drive) && (drive <= 'z')) {
608  wchar_t bufW[MAXPATHLEN + 1];
609  if (driveExists(drive) &&
610  _wgetdcwd(drive - 'a' + 1, bufW, MAXPATHLEN)) {
611  result = getConventionalPath(utf16to8(bufW));
612  if (result.back() != '/') {
613  result += '/';
614  }
615  if (path.size() > 2) {
616  string_view tmp = path.substr(2);
617  result.append(tmp.data(), tmp.size());
618  }
619  }
620  }
621  }
622 #endif
623  return result;
624 }
625 
626 bool getStat(const std::string& filename, Stat& st)
627 {
628 #ifdef _WIN32
629  std::string filename2 = filename;
630  // workaround for VC++: strip trailing slashes (but keep it if it's the
631  // only character in the path)
632  if (auto pos = filename2.find_last_not_of('/'); pos != string::npos) {
633  filename2.resize(pos + 1);
634  } else {
635  // string was either empty or a (sequence of) '/' character(s)
636  if (!filename2.empty()) filename2.resize(1);
637  }
638  return _wstat(utf8to16(filename2).c_str(), &st) == 0;
639 #else
640  return stat(filename.c_str(), &st) == 0;
641 #endif
642 }
643 
644 bool isRegularFile(const Stat& st)
645 {
646  return S_ISREG(st.st_mode);
647 }
648 bool isRegularFile(const std::string& filename)
649 {
650  Stat st;
651  return getStat(filename, st) && isRegularFile(st);
652 }
653 
654 bool isDirectory(const Stat& st)
655 {
656  return S_ISDIR(st.st_mode);
657 }
658 
659 bool isDirectory(const std::string& directory)
660 {
661  Stat st;
662  return getStat(directory, st) && isDirectory(st);
663 }
664 
665 bool exists(const std::string& filename)
666 {
667  Stat st; // dummy
668  return getStat(filename, st);
669 }
670 
671 static unsigned getNextNum(dirent* d, string_view prefix, string_view extension,
672  unsigned nofdigits)
673 {
674  auto extensionLen = extension.size();
675  auto prefixLen = prefix.size();
676  string_view name(d->d_name);
677 
678  if ((name.size() != (prefixLen + nofdigits + extensionLen)) ||
679  (name.substr(0, prefixLen) != prefix) ||
680  (name.substr(prefixLen + nofdigits, extensionLen) != extension)) {
681  return 0;
682  }
683  try {
684  return StringOp::fast_stou(name.substr(prefixLen, nofdigits));
685  } catch (std::invalid_argument&) {
686  return 0;
687  }
688 }
689 
691  string_view directory, string_view prefix, string_view extension)
692 {
693  const unsigned nofdigits = 4;
694 
695  unsigned max_num = 0;
696 
697  string dirName = strCat(getUserOpenMSXDir(), '/', directory);
698  try {
699  mkdirp(dirName);
700  } catch (FileException&) {
701  // ignore
702  }
703 
704  ReadDir dir(dirName);
705  while (auto* d = dir.getEntry()) {
706  max_num = std::max(max_num, getNextNum(d, prefix, extension, nofdigits));
707  }
708 
709  std::ostringstream os;
710  os << dirName << '/' << prefix;
711  os.width(nofdigits);
712  os.fill('0');
713  os << (max_num + 1) << extension;
714  return os.str();
715 }
716 
718  string_view argument, string_view directory,
719  string_view prefix, string_view extension)
720 {
721  if (argument.empty()) {
722  // directory is also created when needed
723  return getNextNumberedFileName(directory, prefix, extension);
724  }
725 
726  string filename(argument);
727  if (getDirName(filename).empty()) {
728  // no dir given, use standard dir (and create it)
729  string dir = strCat(getUserOpenMSXDir(), '/', directory);
730  mkdirp(dir);
731  filename = strCat(dir, '/', filename);
732  } else {
734  }
735 
736  if (!StringOp::endsWith(filename, extension) &&
737  !exists(filename)) {
738  // Expected extension not already given, append it. But only
739  // when the filename without extension doesn't already exist.
740  // Without this exception stuff like 'soundlog start /dev/null'
741  // reports an error " ... error opening file /dev/null.wav."
742  filename.append(extension.data(), extension.size());
743  }
744  return filename;
745 }
746 
747 string getTempDir()
748 {
749 #ifdef _WIN32
750  DWORD len = GetTempPathW(0, nullptr);
751  if (len) {
752  VLA(wchar_t, bufW, (len+1));
753  len = GetTempPathW(len, bufW);
754  if (len) {
755  // Strip last backslash
756  if (bufW[len-1] == L'\\') {
757  bufW[len-1] = L'\0';
758  }
759  return utf16to8(bufW);
760  }
761  }
762  throw FatalError("GetTempPathW failed: ", GetLastError());
763 #elif PLATFORM_ANDROID
764  string result = getSystemDataDir() + "/tmp";
765  return result;
766 #else
767  const char* result = nullptr;
768  if (!result) result = getenv("TMPDIR");
769  if (!result) result = getenv("TMP");
770  if (!result) result = getenv("TEMP");
771  if (!result) {
772  result = "/tmp";
773  }
774  return result;
775 #endif
776 }
777 
778 FILE_t openUniqueFile(const std::string& directory, std::string& filename)
779 {
780 #ifdef _WIN32
781  std::wstring directoryW = utf8to16(directory);
782  wchar_t filenameW[MAX_PATH];
783  if (!GetTempFileNameW(directoryW.c_str(), L"msx", 0, filenameW)) {
784  throw FileException("GetTempFileNameW failed: ", GetLastError());
785  }
786  filename = utf16to8(filenameW);
787  return FILE_t(_wfopen(filenameW, L"wb"));
788 #else
789  filename = directory + "/XXXXXX";
790  auto oldMask = umask(S_IRWXO | S_IRWXG);
791  int fd = mkstemp(const_cast<char*>(filename.c_str()));
792  umask(oldMask);
793  if (fd == -1) {
794  throw FileException("Coundn't get temp file name");
795  }
796  return FILE_t(fdopen(fd, "wb"));
797 #endif
798 }
799 
800 } // namespace openmsx::FileOperations
one_of.hh
FileException.hh
openmsx.hh
StringOp::startsWith
bool startsWith(string_view total, string_view part)
Definition: StringOp.cc:71
utf8
Definition: utf8_checked.hh:36
openmsx::FileOperations::isDirectory
bool isDirectory(const std::string &directory)
Is this a directory?
Definition: FileOperations.cc:659
openmsx::FileOperations::FILE_t
std::unique_ptr< FILE, FClose > FILE_t
Definition: FileOperations.hh:17
openmsx::FileOperations::deleteRecursive
int deleteRecursive(const std::string &path)
Recurively delete a file or directory and (in case of a directory) all its sub-components.
Definition: FileOperations.cc:323
statp.hh
MAXPATHLEN
#define MAXPATHLEN
Definition: FileOperations.cc:36
openmsx::ReadDir
Simple wrapper around opendir() / readdir() / closedir() functions.
Definition: ReadDir.hh:16
utf8::unchecked::size
size_t size(std::string_view utf8)
Definition: utf8_unchecked.hh:227
openmsx::FileOperations::isAbsolutePath
bool isAbsolutePath(string_view path)
Checks whether it's a absolute path or not.
Definition: FileOperations.cc:491
openmsx::FileOperations::getStat
bool getStat(const std::string &filename, Stat &st)
Call stat() and return the stat structure.
Definition: FileOperations.cc:626
openmsx::FileOperations::Stat
struct stat Stat
Definition: FileOperations.hh:218
openmsx::FileOperations::getAbsolutePath
string getAbsolutePath(string_view path)
Transform given path into an absolute path.
Definition: FileOperations.cc:480
utf8::utf8to16
u16bit_iterator utf8to16(octet_iterator start, octet_iterator end, u16bit_iterator result)
Definition: utf8_checked.hh:227
gl::length
T length(const vecN< N, T > &x)
Definition: gl_vec.hh:348
openmsx::FileOperations::unlink
int unlink(const std::string &path)
Call unlink() in a platform-independent manner.
Definition: FileOperations.cc:282
openmsx::FileOperations::getNativePath
string getNativePath(string_view path)
Returns the path in native path-delimiter.
Definition: FileOperations.cc:444
StringOp::fast_stou
unsigned fast_stou(string_view s)
Definition: StringOp.cc:258
ranges.hh
openmsx::L
@ L
Definition: CPUCore.cc:204
utf8::iterator
Definition: utf8_checked.hh:265
openmsx::ReadDir::getEntry
struct dirent * getEntry()
Get directory entry for next file.
Definition: ReadDir.cc:17
openmsx::FileOperations::mkdirp
void mkdirp(string_view path_)
Acts like the unix command "mkdir -p".
Definition: FileOperations.cc:248
openmsx::FileOperations::openofstream
void openofstream(std::ofstream &stream, const std::string &filename, std::ios_base::openmode mode)
Open an ofstream in a platform-independent manner.
Definition: FileOperations.cc:378
systemfuncs.hh
openmsx::FileOperations::getSystemDataDir
string getSystemDataDir()
Get system directory.
Definition: FileOperations.cc:556
openmsx::FileOperations::getCurrentWorkingDirectory
string getCurrentWorkingDirectory()
Returns the current working directory.
Definition: FileOperations.cc:461
ReadDir.hh
StringOp::endsWith
bool endsWith(string_view total, string_view part)
Definition: StringOp.cc:81
vla.hh
openmsx::FileOperations::getDirName
string_view getDirName(string_view path)
Returns the directory portion of a path.
Definition: FileOperations.cc:398
openmsx::FileOperations::getUserDataDir
string getUserDataDir()
Get the openMSX data dir in the user's home directory.
Definition: FileOperations.cc:549
unistdp.hh
openmsx::FileOperations
Definition: FileOperations.cc:77
openmsx::FileOperations::expandTilde
string expandTilde(string_view path)
Expand the '~' character to the users home directory.
Definition: FileOperations.cc:195
one_of
Definition: one_of.hh:7
openmsx::filename
constexpr const char *const filename
Definition: FirmwareSwitch.cc:10
ranges::remove
auto remove(ForwardRange &&range, const T &value)
Definition: ranges.hh:167
openmsx::FileOperations::getUserOpenMSXDir
const string & getUserOpenMSXDir()
Get the openMSX dir in the user's home directory.
Definition: FileOperations.cc:535
openmsx::FileException
Definition: FileException.hh:9
openmsx::FileOperations::expandCurrentDirFromDrive
string expandCurrentDirFromDrive(string_view path)
Get the current directory of the specified drive Linux: just return an empty string.
Definition: FileOperations.cc:599
build-info.hh
openmsx::FileOperations::parseCommandFileArgument
string parseCommandFileArgument(string_view argument, string_view directory, string_view prefix, string_view extension)
Helper function for parsing filename arguments in Tcl commands.
Definition: FileOperations.cc:717
openmsx::FileOperations::openUniqueFile
FILE_t openUniqueFile(const std::string &directory, std::string &filename)
Open a new file with a unique name in the provided directory.
Definition: FileOperations.cc:778
openmsx::FatalError
Definition: MSXException.hh:31
openmsx::FileOperations::getTempDir
string getTempDir()
Get the name of the temp directory on the system.
Definition: FileOperations.cc:747
utf8_checked.hh
openmsx::FileOperations::openFile
FILE_t openFile(const std::string &filename, const std::string &mode)
Call fopen() in a platform-independent manner.
Definition: FileOperations.cc:353
FileOperations.hh
StringOp.hh
openmsx::FileOperations::rmdir
int rmdir(const std::string &path)
Call rmdir() in a platform-independent manner.
Definition: FileOperations.cc:291
ranges::replace
void replace(ForwardRange &&range, const T &old_value, const T &new_value)
Definition: ranges.hh:179
strCat.hh
openmsx::FileOperations::getUserHomeDir
string getUserHomeDir(string_view username)
Get user's home directory.
Definition: FileOperations.cc:505
openmsx::FileOperations::getConventionalPath
const std::string & getConventionalPath(const std::string &path)
Returns the path in conventional path-delimiter.
Definition: FileOperations.hh:148
ad_printf
#define ad_printf(...)
Definition: openmsx.hh:11
gl::max
vecN< N, T > max(const vecN< N, T > &x, const vecN< N, T > &y)
Definition: gl_vec.hh:292
join
detail::Joiner< Collection, Separator > join(Collection &&col, Separator &&sep)
Definition: join.hh:60
openmsx::FileOperations::exists
bool exists(const std::string &filename)
Does this file (directory) exists?
Definition: FileOperations.cc:665
utf8::utf16to8
octet_iterator utf16to8(u16bit_iterator start, u16bit_iterator end, octet_iterator result)
Definition: utf8_checked.hh:204
openmsx::FileOperations::getExtension
string_view getExtension(string_view path)
Returns the extension portion of a path.
Definition: FileOperations.cc:406
openmsx::FileOperations::mkdir
void mkdir(const string &path, mode_t mode)
Create the specified directory.
Definition: FileOperations.cc:220
VLA
#define VLA(TYPE, NAME, LENGTH)
Definition: vla.hh:10
openmsx::FileOperations::isRegularFile
bool isRegularFile(const std::string &filename)
Is this a regular file (no directory, device, ..)?
Definition: FileOperations.cc:648
strCat
std::string strCat(Ts &&...ts)
Definition: strCat.hh:573
openmsx::FileOperations::stripExtension
string_view stripExtension(string_view path)
Returns the path without extension.
Definition: FileOperations.cc:415
openmsx::FileOperations::getNextNumberedFileName
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,...
Definition: FileOperations.cc:690
openmsx::FileOperations::getFilename
string_view getFilename(string_view path)
Returns the file portion of a path name.
Definition: FileOperations.cc:390