| 1306 | } |
| 1307 | |
| 1308 | String FileSystem::GetExecutablePath() |
| 1309 | { |
| 1310 | #if defined(DEATH_TARGET_EMSCRIPTEN) |
| 1311 | return "/"_s; |
| 1312 | #elif defined(DEATH_TARGET_APPLE) |
| 1313 | // Get path size (need to set it to 0 to avoid filling nullptr with random data and crashing) |
| 1314 | std::uint32_t size = 0; |
| 1315 | if (_NSGetExecutablePath(nullptr, &size) != -1) { |
| 1316 | return {}; |
| 1317 | } |
| 1318 | |
| 1319 | // Allocate proper size and get the path. The size includes a null terminator which the String handles on its own, so subtract it |
| 1320 | String path{NoInit, size - 1}; |
| 1321 | if (_NSGetExecutablePath(path.data(), &size) != 0) { |
| 1322 | return {}; |
| 1323 | } |
| 1324 | return path; |
| 1325 | #elif defined(__FreeBSD__) |
| 1326 | std::size_t size; |
| 1327 | static const std::int32_t mib[4] = { CTL_KERN, KERN_PROC, KERN_PROC_PATHNAME, -1 }; |
| 1328 | sysctl(mib, 4, nullptr, &size, nullptr, 0); |
| 1329 | String path{NoInit, size}; |
| 1330 | sysctl(mib, 4, path.data(), &size, nullptr, 0); |
| 1331 | return path; |
| 1332 | #elif defined(DEATH_TARGET_UNIX) |
| 1333 | // Reallocate like hell until we have enough place to store the path. Can't use lstat because |
| 1334 | // the /proc/self/exe symlink is not a real symlink and so stat::st_size returns 0. |
| 1335 | static const char self[] = "/proc/self/exe"; |
| 1336 | Array<char> path; |
| 1337 | arrayResize(path, NoInit, 16); |
| 1338 | ssize_t size; |
| 1339 | while ((size = ::readlink(self, path, path.size())) == ssize_t(path.size())) { |
| 1340 | arrayResize(path, NoInit, path.size() * 2); |
| 1341 | } |
| 1342 | |
| 1343 | if (size == -1) { |
| 1344 | LOGE("Cannot read \"{}\" with error {}{}", self, errno, __GetUnixErrorSuffix(errno)); |
| 1345 | return {}; |
| 1346 | } |
| 1347 | |
| 1348 | // readlink() doesn't put the null terminator into the array, do it ourselves. The above loop guarantees |
| 1349 | // that path.size() is always larger than size - if it would be equal, we'd try once more with a larger buffer |
| 1350 | path[size] = '\0'; |
| 1351 | const auto deleter = path.deleter(); |
| 1352 | return String{path.release(), std::size_t(size), deleter}; |
| 1353 | #elif defined(DEATH_TARGET_WINDOWS) && !defined(DEATH_TARGET_WINDOWS_RT) |
| 1354 | wchar_t path[MaxPathLength + 1]; |
| 1355 | // Returns size *without* the null terminator |
| 1356 | const std::size_t size = ::GetModuleFileNameW(NULL, path, DWORD(arraySize(path))); |
| 1357 | return Utf8::FromUtf16(arrayView(path, size)); |
| 1358 | #else |
| 1359 | return {}; |
| 1360 | #endif |
| 1361 | } |
| 1362 | |
| 1363 | String FileSystem::GetSavePath(StringView applicationName) |
| 1364 | { |
nothing calls this directly
no test coverage detected