You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

735 lines
19KB

  1. #include <thread>
  2. #include <regex>
  3. #include <chrono>
  4. #include <ghc/filesystem.hpp>
  5. #include <dirent.h>
  6. #include <sys/stat.h>
  7. #include <cxxabi.h> // for __cxxabiv1::__cxa_demangle
  8. #if defined ARCH_LIN || defined ARCH_MAC
  9. #include <pthread.h>
  10. #include <time.h> // for clock_gettime
  11. #include <sched.h>
  12. #include <execinfo.h> // for backtrace and backtrace_symbols
  13. #include <unistd.h> // for execl
  14. #include <sys/utsname.h>
  15. #endif
  16. #if defined ARCH_MAC
  17. #include <mach/mach_init.h>
  18. #include <mach/thread_act.h>
  19. #include <mach/clock.h>
  20. #include <mach/mach.h>
  21. #endif
  22. #if defined ARCH_WIN
  23. #include <windows.h>
  24. #include <shellapi.h>
  25. #include <processthreadsapi.h>
  26. #include <dbghelp.h>
  27. #endif
  28. #include <archive.h>
  29. #include <archive_entry.h>
  30. #include <system.hpp>
  31. #include <string.hpp>
  32. /*
  33. In C++17, this will be `std::filesystem`
  34. Important: When using `fs::path`, always convert strings to UTF-8 using
  35. fs::path p = fs::u8path(s);
  36. In fact, it's best to work only with strings to avoid forgetting to decode a string as UTF-8.
  37. The need to do this is a fatal flaw of `fs::path`, but at least `std::filesystem` has some helpful operations.
  38. */
  39. namespace fs = ghc::filesystem;
  40. namespace rack {
  41. namespace system {
  42. std::string join(const std::string& path1, const std::string& path2) {
  43. return (fs::u8path(path1) / fs::u8path(path2)).generic_u8string();
  44. }
  45. std::list<std::string> getEntries(const std::string& dirPath, int depth) {
  46. try {
  47. std::list<std::string> entries;
  48. for (auto& entry : fs::directory_iterator(fs::u8path(dirPath))) {
  49. std::string subEntry = entry.path().generic_u8string();
  50. entries.push_back(subEntry);
  51. // Recurse if depth > 0 (limited recursion) or depth < 0 (infinite recursion).
  52. if (depth != 0) {
  53. if (fs::is_directory(entry.path())) {
  54. std::list<std::string> subEntries = getEntries(subEntry, depth - 1);
  55. entries.splice(entries.end(), subEntries);
  56. }
  57. }
  58. }
  59. return entries;
  60. }
  61. catch (fs::filesystem_error& e) {
  62. throw Exception("%s", e.what());
  63. }
  64. }
  65. bool exists(const std::string& path) {
  66. try {
  67. return fs::exists(fs::u8path(path));
  68. }
  69. catch (fs::filesystem_error& e) {
  70. throw Exception("%s", e.what());
  71. }
  72. }
  73. bool isFile(const std::string& path) {
  74. try {
  75. return fs::is_regular_file(fs::u8path(path));
  76. }
  77. catch (fs::filesystem_error& e) {
  78. throw Exception("%s", e.what());
  79. }
  80. }
  81. bool isDir(const std::string& path) {
  82. try {
  83. return fs::is_directory(fs::u8path(path));
  84. }
  85. catch (fs::filesystem_error& e) {
  86. throw Exception("%s", e.what());
  87. }
  88. }
  89. uint64_t getFileSize(const std::string& path) {
  90. try {
  91. return fs::file_size(fs::u8path(path));
  92. }
  93. catch (fs::filesystem_error& e) {
  94. throw Exception("%s", e.what());
  95. }
  96. }
  97. void rename(const std::string& srcPath, const std::string& destPath) {
  98. try {
  99. fs::rename(fs::u8path(srcPath), fs::u8path(destPath));
  100. }
  101. catch (fs::filesystem_error& e) {
  102. throw Exception("%s", e.what());
  103. }
  104. }
  105. void copy(const std::string& srcPath, const std::string& destPath) {
  106. try {
  107. fs::copy(fs::u8path(srcPath), fs::u8path(destPath), fs::copy_options::recursive);
  108. }
  109. catch (fs::filesystem_error& e) {
  110. throw Exception("%s", e.what());
  111. }
  112. }
  113. bool createDir(const std::string& path) {
  114. try {
  115. return fs::create_directory(fs::u8path(path));
  116. }
  117. catch (fs::filesystem_error& e) {
  118. throw Exception("%s", e.what());
  119. }
  120. }
  121. bool createDirs(const std::string& path) {
  122. try {
  123. return fs::create_directories(fs::u8path(path));
  124. }
  125. catch (fs::filesystem_error& e) {
  126. throw Exception("%s", e.what());
  127. }
  128. }
  129. bool remove(const std::string& path) {
  130. try {
  131. return fs::remove(fs::u8path(path));
  132. }
  133. catch (fs::filesystem_error& e) {
  134. throw Exception("%s", e.what());
  135. }
  136. }
  137. int removeRecursively(const std::string& path) {
  138. try {
  139. return fs::remove_all(fs::u8path(path));
  140. }
  141. catch (fs::filesystem_error& e) {
  142. throw Exception("%s", e.what());
  143. }
  144. }
  145. std::string getWorkingDir() {
  146. try {
  147. return fs::current_path().generic_u8string();
  148. }
  149. catch (fs::filesystem_error& e) {
  150. throw Exception("%s", e.what());
  151. }
  152. }
  153. void setWorkingDir(const std::string& path) {
  154. try {
  155. fs::current_path(fs::u8path(path));
  156. }
  157. catch (fs::filesystem_error& e) {
  158. throw Exception("%s", e.what());
  159. }
  160. }
  161. std::string getTempDir() {
  162. try {
  163. return fs::temp_directory_path().generic_u8string();
  164. }
  165. catch (fs::filesystem_error& e) {
  166. throw Exception("%s", e.what());
  167. }
  168. }
  169. std::string getAbsolute(const std::string& path) {
  170. try {
  171. return fs::absolute(fs::u8path(path)).generic_u8string();
  172. }
  173. catch (fs::filesystem_error& e) {
  174. throw Exception("%s", e.what());
  175. }
  176. }
  177. std::string getCanonical(const std::string& path) {
  178. try {
  179. return fs::canonical(fs::u8path(path)).generic_u8string();
  180. }
  181. catch (fs::filesystem_error& e) {
  182. throw Exception("%s", e.what());
  183. }
  184. }
  185. std::string getDir(const std::string& path) {
  186. try {
  187. return fs::u8path(path).parent_path().generic_u8string();
  188. }
  189. catch (fs::filesystem_error& e) {
  190. throw Exception("%s", e.what());
  191. }
  192. }
  193. std::string getFilename(const std::string& path) {
  194. try {
  195. return fs::u8path(path).filename().generic_u8string();
  196. }
  197. catch (fs::filesystem_error& e) {
  198. throw Exception("%s", e.what());
  199. }
  200. }
  201. std::string getStem(const std::string& path) {
  202. try {
  203. return fs::u8path(path).stem().generic_u8string();
  204. }
  205. catch (fs::filesystem_error& e) {
  206. throw Exception("%s", e.what());
  207. }
  208. }
  209. std::string getExtension(const std::string& path) {
  210. try {
  211. return fs::u8path(path).extension().generic_u8string();
  212. }
  213. catch (fs::filesystem_error& e) {
  214. throw Exception("%s", e.what());
  215. }
  216. }
  217. /** Returns `p` in relative path form, relative to `base`
  218. Limitation: `p` must be a descendant of `base`. Doesn't support adding `../` to the return path.
  219. */
  220. static std::string getRelativePath(std::string path, std::string base) {
  221. try {
  222. path = fs::absolute(fs::u8path(path)).generic_u8string();
  223. base = fs::absolute(fs::u8path(base)).generic_u8string();
  224. }
  225. catch (fs::filesystem_error& e) {
  226. throw Exception("%s", e.what());
  227. }
  228. if (path.size() < base.size())
  229. throw Exception("getRelativePath() error: path is shorter than base");
  230. if (!std::equal(base.begin(), base.end(), path.begin()))
  231. throw Exception("getRelativePath() error: path does not begin with base");
  232. // If path == base, this correctly returns "."
  233. return "." + std::string(path.begin() + base.size(), path.end());
  234. }
  235. static la_ssize_t archiveWriteVectorCallback(struct archive* a, void* client_data, const void* buffer, size_t length) {
  236. assert(client_data);
  237. std::vector<uint8_t>& data = *((std::vector<uint8_t>*) client_data);
  238. uint8_t* buf = (uint8_t*) buffer;
  239. data.insert(data.end(), buf, buf + length);
  240. return length;
  241. }
  242. static void archiveDir(const std::string& archivePath, std::vector<uint8_t>* archiveData, const std::string& dirPath, int compressionLevel) {
  243. // Based on minitar.c create() in libarchive examples
  244. int r;
  245. // Open archive for writing
  246. struct archive* a = archive_write_new();
  247. DEFER({archive_write_free(a);});
  248. // For some reason libarchive adds 10k of padding to archive_write_open() (but not archive_write_open_filename()) unless this is set to 0.
  249. archive_write_set_bytes_per_block(a, 0);
  250. archive_write_set_format_ustar(a);
  251. archive_write_add_filter_zstd(a);
  252. assert(0 <= compressionLevel && compressionLevel <= 19);
  253. r = archive_write_set_filter_option(a, NULL, "compression-level", std::to_string(compressionLevel).c_str());
  254. if (r < ARCHIVE_OK)
  255. throw Exception("Archiver could not set filter option: %s", archive_error_string(a));
  256. if (archiveData) {
  257. // Open vector
  258. archive_write_open(a, (void*) archiveData, NULL, archiveWriteVectorCallback, NULL);
  259. }
  260. else {
  261. // Open file
  262. #if defined ARCH_WIN
  263. r = archive_write_open_filename_w(a, string::UTF8toUTF16(archivePath).c_str());
  264. #else
  265. r = archive_write_open_filename(a, archivePath.c_str());
  266. #endif
  267. if (r < ARCHIVE_OK)
  268. throw Exception("Archiver could not open archive %s for writing: %s", archivePath.c_str(), archive_error_string(a));
  269. }
  270. DEFER({archive_write_close(a);});
  271. // Open dir for reading
  272. struct archive* disk = archive_read_disk_new();
  273. DEFER({archive_read_free(disk);});
  274. #if defined ARCH_WIN
  275. r = archive_read_disk_open_w(disk, string::UTF8toUTF16(dirPath).c_str());
  276. #else
  277. r = archive_read_disk_open(disk, dirPath.c_str());
  278. #endif
  279. if (r < ARCHIVE_OK)
  280. throw Exception("Archiver could not open dir %s for reading: %s", dirPath.c_str(), archive_error_string(a));
  281. DEFER({archive_read_close(a);});
  282. // Iterate dir
  283. for (;;) {
  284. struct archive_entry* entry = archive_entry_new();
  285. DEFER({archive_entry_free(entry);});
  286. r = archive_read_next_header2(disk, entry);
  287. if (r == ARCHIVE_EOF)
  288. break;
  289. if (r < ARCHIVE_OK)
  290. throw Exception("Archiver could not get next entry from archive: %s", archive_error_string(disk));
  291. // Recurse dirs
  292. archive_read_disk_descend(disk);
  293. // Convert absolute path to relative path
  294. std::string entryPath;
  295. #if defined ARCH_WIN
  296. entryPath = string::UTF16toUTF8(archive_entry_pathname_w(entry));
  297. #else
  298. entryPath = archive_entry_pathname(entry);
  299. #endif
  300. entryPath = getRelativePath(entryPath, dirPath);
  301. #if defined ARCH_WIN
  302. // FIXME This doesn't seem to set UTF-8 paths on Windows.
  303. archive_entry_copy_pathname_w(entry, string::UTF8toUTF16(entryPath).c_str());
  304. #else
  305. archive_entry_set_pathname(entry, entryPath.c_str());
  306. #endif
  307. // Write file to archive
  308. r = archive_write_header(a, entry);
  309. if (r < ARCHIVE_OK)
  310. throw Exception("Archiver could not write entry to archive: %s", archive_error_string(a));
  311. // Manually copy data
  312. #if defined ARCH_WIN
  313. std::string entrySourcePath = string::UTF16toUTF8(archive_entry_sourcepath_w(entry));
  314. #else
  315. std::string entrySourcePath = archive_entry_sourcepath(entry);
  316. #endif
  317. FILE* f = std::fopen(entrySourcePath.c_str(), "rb");
  318. DEFER({std::fclose(f);});
  319. char buf[1 << 16];
  320. ssize_t len;
  321. while ((len = std::fread(buf, 1, sizeof(buf), f)) > 0) {
  322. archive_write_data(a, buf, len);
  323. }
  324. }
  325. }
  326. void archiveDir(const std::string& archivePath, const std::string& dirPath, int compressionLevel) {
  327. archiveDir(archivePath, NULL, dirPath, compressionLevel);
  328. }
  329. std::vector<uint8_t> archiveDir(const std::string& dirPath, int compressionLevel) {
  330. std::vector<uint8_t> archiveData;
  331. archiveDir("", &archiveData, dirPath, compressionLevel);
  332. return archiveData;
  333. }
  334. struct ArchiveReadVectorData {
  335. const std::vector<uint8_t>* data = NULL;
  336. size_t pos = 0;
  337. };
  338. static la_ssize_t archiveReadVectorCallback(struct archive *a, void* client_data, const void** buffer) {
  339. assert(client_data);
  340. ArchiveReadVectorData* arvd = (ArchiveReadVectorData*) client_data;
  341. assert(arvd->data);
  342. const std::vector<uint8_t>& data = *arvd->data;
  343. *buffer = &data[arvd->pos];
  344. // Read up to some block size of bytes
  345. size_t len = std::min(data.size() - arvd->pos, size_t(1 << 16));
  346. arvd->pos += len;
  347. return len;
  348. }
  349. static void unarchiveToDir(const std::string& archivePath, const std::vector<uint8_t>* archiveData, const std::string& dirPath) {
  350. // Based on minitar.c extract() in libarchive examples
  351. int r;
  352. // Open archive for reading
  353. struct archive* a = archive_read_new();
  354. DEFER({archive_read_free(a);});
  355. archive_read_support_filter_zstd(a);
  356. // archive_read_support_filter_all(a);
  357. archive_read_support_format_tar(a);
  358. // archive_read_support_format_all(a);
  359. ArchiveReadVectorData arvd;
  360. if (archiveData) {
  361. // Open vector
  362. arvd.data = archiveData;
  363. archive_read_open(a, &arvd, NULL, archiveReadVectorCallback, NULL);
  364. }
  365. else {
  366. // Open file
  367. #if defined ARCH_WIN
  368. r = archive_read_open_filename_w(a, string::UTF8toUTF16(archivePath).c_str(), 1 << 16);
  369. #else
  370. r = archive_read_open_filename(a, archivePath.c_str(), 1 << 16);
  371. #endif
  372. if (r < ARCHIVE_OK)
  373. throw Exception("Unarchiver could not open archive %s: %s", archivePath.c_str(), archive_error_string(a));
  374. }
  375. DEFER({archive_read_close(a);});
  376. // Open dir for writing
  377. struct archive* disk = archive_write_disk_new();
  378. DEFER({archive_write_free(disk);});
  379. int flags = ARCHIVE_EXTRACT_TIME;
  380. archive_write_disk_set_options(disk, flags);
  381. DEFER({archive_write_close(disk);});
  382. // Iterate archive
  383. for (;;) {
  384. // Get next entry
  385. struct archive_entry* entry;
  386. r = archive_read_next_header(a, &entry);
  387. if (r == ARCHIVE_EOF)
  388. break;
  389. if (r < ARCHIVE_OK)
  390. throw Exception("Unarchiver could not read entry from archive: %s", archive_error_string(a));
  391. // Convert relative pathname to absolute based on dirPath
  392. std::string entryPath = archive_entry_pathname(entry);
  393. if (!fs::u8path(entryPath).is_relative())
  394. throw Exception("Unarchiver does not support absolute tar paths: %s", entryPath.c_str());
  395. entryPath = (fs::u8path(dirPath) / fs::u8path(entryPath)).generic_u8string();
  396. #if defined ARCH_WIN
  397. archive_entry_copy_pathname_w(entry, string::UTF8toUTF16(entryPath).c_str());
  398. #else
  399. archive_entry_set_pathname(entry, entryPath.c_str());
  400. #endif
  401. // Write entry to disk
  402. r = archive_write_header(disk, entry);
  403. if (r < ARCHIVE_OK)
  404. throw Exception("Unarchiver could not write file to dir: %s", archive_error_string(disk));
  405. // Copy data to file
  406. for (;;) {
  407. const void* buf;
  408. size_t size;
  409. int64_t offset;
  410. // Read data from archive
  411. r = archive_read_data_block(a, &buf, &size, &offset);
  412. if (r == ARCHIVE_EOF)
  413. break;
  414. if (r < ARCHIVE_OK)
  415. throw Exception("Unarchiver could not read data from archive: %s", archive_error_string(a));
  416. // Write data to file
  417. r = archive_write_data_block(disk, buf, size, offset);
  418. if (r < ARCHIVE_OK)
  419. throw Exception("Unarchiver could not write data to file: %s", archive_error_string(disk));
  420. }
  421. // Close file
  422. r = archive_write_finish_entry(disk);
  423. if (r < ARCHIVE_OK)
  424. throw Exception("Unarchiver could not close file: %s", archive_error_string(disk));
  425. }
  426. }
  427. void unarchiveToDir(const std::string& archivePath, const std::string& dirPath) {
  428. unarchiveToDir(archivePath, NULL, dirPath);
  429. }
  430. void unarchiveToDir(const std::vector<uint8_t>& archiveData, const std::string& dirPath) {
  431. unarchiveToDir("", &archiveData, dirPath);
  432. }
  433. int getLogicalCoreCount() {
  434. return std::thread::hardware_concurrency();
  435. }
  436. void setThreadName(const std::string& name) {
  437. #if defined ARCH_LIN
  438. pthread_setname_np(pthread_self(), name.c_str());
  439. #elif defined ARCH_WIN
  440. // Unsupported on Windows
  441. #endif
  442. }
  443. std::string getStackTrace() {
  444. int stackLen = 128;
  445. void* stack[stackLen];
  446. std::string s;
  447. #if defined ARCH_LIN || defined ARCH_MAC
  448. stackLen = backtrace(stack, stackLen);
  449. char** strings = backtrace_symbols(stack, stackLen);
  450. // Skip the first line because it's this function.
  451. for (int i = 1; i < stackLen; i++) {
  452. s += string::f("%d: ", stackLen - i - 1);
  453. std::string line = strings[i];
  454. #if 0
  455. // Parse line
  456. std::regex r(R"((.*)\((.*)\+(.*)\) (.*))");
  457. std::smatch match;
  458. if (std::regex_search(line, match, r)) {
  459. s += match[1].str();
  460. s += "(";
  461. std::string symbol = match[2].str();
  462. // Demangle symbol
  463. char* symbolD = __cxxabiv1::__cxa_demangle(symbol.c_str(), NULL, NULL, NULL);
  464. if (symbolD) {
  465. symbol = symbolD;
  466. free(symbolD);
  467. }
  468. s += symbol;
  469. s += "+";
  470. s += match[3].str();
  471. s += ")";
  472. }
  473. #else
  474. s += line;
  475. #endif
  476. s += "\n";
  477. }
  478. free(strings);
  479. #elif defined ARCH_WIN
  480. HANDLE process = GetCurrentProcess();
  481. SymInitialize(process, NULL, true);
  482. stackLen = CaptureStackBackTrace(0, stackLen, stack, NULL);
  483. SYMBOL_INFO* symbol = (SYMBOL_INFO*) calloc(sizeof(SYMBOL_INFO) + 256, 1);
  484. symbol->MaxNameLen = 255;
  485. symbol->SizeOfStruct = sizeof(SYMBOL_INFO);
  486. for (int i = 1; i < stackLen; i++) {
  487. SymFromAddr(process, (DWORD64) stack[i], 0, symbol);
  488. s += string::f("%d: %s 0x%" PRIx64 "\n", stackLen - i - 1, symbol->Name, symbol->Address);
  489. }
  490. free(symbol);
  491. #endif
  492. return s;
  493. }
  494. #if defined ARCH_WIN
  495. static int64_t startCounter = 0;
  496. static double counterTime = 0.0;
  497. #endif
  498. #if defined ARCH_LIN || defined ARCH_MAC
  499. static int64_t startTime = 0;
  500. #endif
  501. static void initTime() {
  502. #if defined ARCH_WIN
  503. LARGE_INTEGER counter;
  504. QueryPerformanceCounter(&counter);
  505. startCounter = counter.QuadPart;
  506. LARGE_INTEGER frequency;
  507. QueryPerformanceFrequency(&frequency);
  508. counterTime = 1.0 / frequency.QuadPart;
  509. #endif
  510. #if defined ARCH_LIN
  511. struct timespec ts;
  512. clock_gettime(CLOCK_MONOTONIC_RAW, &ts);
  513. startTime = int64_t(ts.tv_sec) * 1000000000LL + ts.tv_nsec;
  514. #endif
  515. #if defined ARCH_MAC
  516. clock_serv_t cclock;
  517. mach_timespec_t mts;
  518. host_get_clock_service(mach_host_self(), SYSTEM_CLOCK, &cclock);
  519. clock_get_time(cclock, &mts);
  520. mach_port_deallocate(mach_task_self(), cclock);
  521. startTime = int64_t(mts.tv_sec) * 1000000000LL + mts.tv_nsec;
  522. #endif
  523. }
  524. double getTime() {
  525. #if defined ARCH_WIN
  526. LARGE_INTEGER counter;
  527. QueryPerformanceCounter(&counter);
  528. return (counter.QuadPart - startCounter) * counterTime;
  529. #endif
  530. #if defined ARCH_LIN
  531. struct timespec ts;
  532. clock_gettime(CLOCK_MONOTONIC_RAW, &ts);
  533. int64_t time = int64_t(ts.tv_sec) * 1000000000LL + ts.tv_nsec;
  534. return (time - startTime) / 1e9;
  535. #endif
  536. #if defined ARCH_MAC
  537. clock_serv_t cclock;
  538. mach_timespec_t mts;
  539. host_get_clock_service(mach_host_self(), SYSTEM_CLOCK, &cclock);
  540. clock_get_time(cclock, &mts);
  541. mach_port_deallocate(mach_task_self(), cclock);
  542. int64_t time = int64_t(mts.tv_sec) * 1000000000LL + mts.tv_nsec;
  543. return (time - startTime) / 1e9;
  544. #endif
  545. }
  546. double getUnixTime() {
  547. // This is not guaranteed to return the time since 1970 in C++11. (It only does in C++20).
  548. // However, it does on all platforms I care about.
  549. auto duration = std::chrono::system_clock::now().time_since_epoch();
  550. return std::chrono::duration<double>(duration).count();
  551. }
  552. std::string getOperatingSystemInfo() {
  553. #if defined ARCH_LIN || defined ARCH_MAC
  554. struct utsname u;
  555. uname(&u);
  556. return string::f("%s %s %s %s", u.sysname, u.release, u.version, u.machine);
  557. #elif defined ARCH_WIN
  558. OSVERSIONINFOW info;
  559. ZeroMemory(&info, sizeof(info));
  560. info.dwOSVersionInfoSize = sizeof(info);
  561. GetVersionExW(&info);
  562. // See https://docs.microsoft.com/en-us/windows/desktop/api/winnt/ns-winnt-_osversioninfoa for a list of Windows version numbers.
  563. return string::f("Windows %lu.%lu", info.dwMajorVersion, info.dwMinorVersion);
  564. #endif
  565. }
  566. void openBrowser(const std::string& url) {
  567. #if defined ARCH_LIN
  568. std::string command = "xdg-open \"" + url + "\"";
  569. (void) std::system(command.c_str());
  570. #endif
  571. #if defined ARCH_MAC
  572. std::string command = "open \"" + url + "\"";
  573. std::system(command.c_str());
  574. #endif
  575. #if defined ARCH_WIN
  576. ShellExecuteW(NULL, L"open", string::UTF8toUTF16(url).c_str(), NULL, NULL, SW_SHOWDEFAULT);
  577. #endif
  578. }
  579. void openDir(const std::string& path) {
  580. #if defined ARCH_LIN
  581. std::string command = "xdg-open \"" + path + "\"";
  582. (void) std::system(command.c_str());
  583. #endif
  584. #if defined ARCH_MAC
  585. std::string command = "open \"" + path + "\"";
  586. std::system(command.c_str());
  587. #endif
  588. #if defined ARCH_WIN
  589. ShellExecuteW(NULL, L"explore", string::UTF8toUTF16(path).c_str(), NULL, NULL, SW_SHOWDEFAULT);
  590. #endif
  591. }
  592. void runProcessDetached(const std::string& path) {
  593. #if defined ARCH_WIN
  594. SHELLEXECUTEINFOW shExInfo;
  595. ZeroMemory(&shExInfo, sizeof(shExInfo));
  596. shExInfo.cbSize = sizeof(shExInfo);
  597. shExInfo.lpVerb = L"runas";
  598. std::wstring pathW = string::UTF8toUTF16(path);
  599. shExInfo.lpFile = pathW.c_str();
  600. shExInfo.nShow = SW_SHOW;
  601. if (ShellExecuteExW(&shExInfo)) {
  602. // Do nothing
  603. }
  604. #else
  605. // Not implemented on Linux or Mac
  606. assert(0);
  607. #endif
  608. }
  609. void init() {
  610. initTime();
  611. }
  612. } // namespace system
  613. } // namespace rack