porting.cpp 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845
  1. /*
  2. Minetest
  3. Copyright (C) 2013 celeron55, Perttu Ahola <celeron55@gmail.com>
  4. This program is free software; you can redistribute it and/or modify
  5. it under the terms of the GNU Lesser General Public License as published by
  6. the Free Software Foundation; either version 2.1 of the License, or
  7. (at your option) any later version.
  8. This program is distributed in the hope that it will be useful,
  9. but WITHOUT ANY WARRANTY; without even the implied warranty of
  10. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  11. GNU Lesser General Public License for more details.
  12. You should have received a copy of the GNU Lesser General Public License along
  13. with this program; if not, write to the Free Software Foundation, Inc.,
  14. 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
  15. */
  16. /*
  17. Random portability stuff
  18. See comments in porting.h
  19. */
  20. #include "porting.h"
  21. #if defined(__FreeBSD__) || defined(__NetBSD__) || defined(__DragonFly__) || defined(__OpenBSD__)
  22. #include <sys/types.h>
  23. #include <sys/sysctl.h>
  24. extern char **environ;
  25. #elif defined(_WIN32)
  26. #include <windows.h>
  27. #include <wincrypt.h>
  28. #include <algorithm>
  29. #include <shlwapi.h>
  30. #include <shellapi.h>
  31. #include <mmsystem.h>
  32. #endif
  33. #if !defined(_WIN32)
  34. #include <unistd.h>
  35. #include <sys/utsname.h>
  36. #if !defined(__ANDROID__)
  37. #include <spawn.h>
  38. #endif
  39. #endif
  40. #if defined(__hpux)
  41. #define _PSTAT64
  42. #include <sys/pstat.h>
  43. #endif
  44. #if defined(__ANDROID__)
  45. #include "porting_android.h"
  46. #endif
  47. #if defined(__APPLE__)
  48. // For _NSGetEnviron()
  49. // Related: https://gitlab.haskell.org/ghc/ghc/issues/2458
  50. #include <crt_externs.h>
  51. #endif
  52. #if defined(__HAIKU__)
  53. #include <FindDirectory.h>
  54. #endif
  55. #include "config.h"
  56. #include "debug.h"
  57. #include "filesys.h"
  58. #include "log.h"
  59. #include "util/string.h"
  60. #include <list>
  61. #include <cstdarg>
  62. #include <cstdio>
  63. #if !defined(SERVER) && defined(_WIN32)
  64. // On Windows export some driver-specific variables to encourage Minetest to be
  65. // executed on the discrete GPU in case of systems with two. Portability is fun.
  66. extern "C" {
  67. __declspec(dllexport) DWORD NvOptimusEnablement = 1;
  68. __declspec(dllexport) DWORD AmdPowerXpressRequestHighPerformance = 1;
  69. }
  70. #endif
  71. namespace porting
  72. {
  73. /*
  74. Signal handler (grabs Ctrl-C on POSIX systems)
  75. */
  76. bool g_killed = false;
  77. bool *signal_handler_killstatus()
  78. {
  79. return &g_killed;
  80. }
  81. #if !defined(_WIN32) // POSIX
  82. #include <signal.h>
  83. void signal_handler(int sig)
  84. {
  85. if (!g_killed) {
  86. if (sig == SIGINT) {
  87. dstream << "INFO: signal_handler(): "
  88. << "Ctrl-C pressed, shutting down." << std::endl;
  89. } else if (sig == SIGTERM) {
  90. dstream << "INFO: signal_handler(): "
  91. << "got SIGTERM, shutting down." << std::endl;
  92. }
  93. // Comment out for less clutter when testing scripts
  94. /*dstream << "INFO: sigint_handler(): "
  95. << "Printing debug stacks" << std::endl;
  96. debug_stacks_print();*/
  97. g_killed = true;
  98. } else {
  99. (void)signal(sig, SIG_DFL);
  100. }
  101. }
  102. void signal_handler_init(void)
  103. {
  104. (void)signal(SIGINT, signal_handler);
  105. (void)signal(SIGTERM, signal_handler);
  106. }
  107. #else // _WIN32
  108. #include <signal.h>
  109. BOOL WINAPI event_handler(DWORD sig)
  110. {
  111. switch (sig) {
  112. case CTRL_C_EVENT:
  113. case CTRL_CLOSE_EVENT:
  114. case CTRL_LOGOFF_EVENT:
  115. case CTRL_SHUTDOWN_EVENT:
  116. if (!g_killed) {
  117. dstream << "INFO: event_handler(): "
  118. << "Ctrl+C, Close Event, Logoff Event or Shutdown Event,"
  119. " shutting down." << std::endl;
  120. g_killed = true;
  121. } else {
  122. (void)signal(SIGINT, SIG_DFL);
  123. }
  124. break;
  125. case CTRL_BREAK_EVENT:
  126. break;
  127. }
  128. return TRUE;
  129. }
  130. void signal_handler_init(void)
  131. {
  132. SetConsoleCtrlHandler((PHANDLER_ROUTINE)event_handler, TRUE);
  133. }
  134. #endif
  135. /*
  136. Path mangler
  137. */
  138. // Default to RUN_IN_PLACE style relative paths
  139. std::string path_share = "..";
  140. std::string path_user = "..";
  141. std::string path_locale = path_share + DIR_DELIM + "locale";
  142. std::string path_cache = path_user + DIR_DELIM + "cache";
  143. std::string getDataPath(const char *subpath)
  144. {
  145. return path_share + DIR_DELIM + subpath;
  146. }
  147. void pathRemoveFile(char *path, char delim)
  148. {
  149. // Remove filename and path delimiter
  150. int i;
  151. for(i = strlen(path)-1; i>=0; i--)
  152. {
  153. if(path[i] == delim)
  154. break;
  155. }
  156. path[i] = 0;
  157. }
  158. bool detectMSVCBuildDir(const std::string &path)
  159. {
  160. const char *ends[] = {
  161. "bin\\Release",
  162. "bin\\MinSizeRel",
  163. "bin\\RelWithDebInfo",
  164. "bin\\Debug",
  165. "bin\\Build",
  166. NULL
  167. };
  168. return (!removeStringEnd(path, ends).empty());
  169. }
  170. std::string get_sysinfo()
  171. {
  172. #ifdef _WIN32
  173. std::ostringstream oss;
  174. LPSTR filePath = new char[MAX_PATH];
  175. UINT blockSize;
  176. VS_FIXEDFILEINFO *fixedFileInfo;
  177. GetSystemDirectoryA(filePath, MAX_PATH);
  178. PathAppendA(filePath, "kernel32.dll");
  179. DWORD dwVersionSize = GetFileVersionInfoSizeA(filePath, NULL);
  180. LPBYTE lpVersionInfo = new BYTE[dwVersionSize];
  181. GetFileVersionInfoA(filePath, 0, dwVersionSize, lpVersionInfo);
  182. VerQueryValueA(lpVersionInfo, "\\", (LPVOID *)&fixedFileInfo, &blockSize);
  183. oss << "Windows/"
  184. << HIWORD(fixedFileInfo->dwProductVersionMS) << '.' // Major
  185. << LOWORD(fixedFileInfo->dwProductVersionMS) << '.' // Minor
  186. << HIWORD(fixedFileInfo->dwProductVersionLS) << ' '; // Build
  187. #ifdef _WIN64
  188. oss << "x86_64";
  189. #else
  190. BOOL is64 = FALSE;
  191. if (IsWow64Process(GetCurrentProcess(), &is64) && is64)
  192. oss << "x86_64"; // 32-bit app on 64-bit OS
  193. else
  194. oss << "x86";
  195. #endif
  196. delete[] lpVersionInfo;
  197. delete[] filePath;
  198. return oss.str();
  199. #else
  200. struct utsname osinfo;
  201. uname(&osinfo);
  202. return std::string(osinfo.sysname) + "/"
  203. + osinfo.release + " " + osinfo.machine;
  204. #endif
  205. }
  206. bool getCurrentWorkingDir(char *buf, size_t len)
  207. {
  208. #ifdef _WIN32
  209. DWORD ret = GetCurrentDirectory(len, buf);
  210. return (ret != 0) && (ret <= len);
  211. #else
  212. return getcwd(buf, len);
  213. #endif
  214. }
  215. bool getExecPathFromProcfs(char *buf, size_t buflen)
  216. {
  217. #ifndef _WIN32
  218. buflen--;
  219. ssize_t len;
  220. if ((len = readlink("/proc/self/exe", buf, buflen)) == -1 &&
  221. (len = readlink("/proc/curproc/file", buf, buflen)) == -1 &&
  222. (len = readlink("/proc/curproc/exe", buf, buflen)) == -1)
  223. return false;
  224. buf[len] = '\0';
  225. return true;
  226. #else
  227. return false;
  228. #endif
  229. }
  230. //// Windows
  231. #if defined(_WIN32)
  232. bool getCurrentExecPath(char *buf, size_t len)
  233. {
  234. DWORD written = GetModuleFileNameA(NULL, buf, len);
  235. if (written == 0 || written == len)
  236. return false;
  237. return true;
  238. }
  239. //// Linux
  240. #elif defined(__linux__)
  241. bool getCurrentExecPath(char *buf, size_t len)
  242. {
  243. if (!getExecPathFromProcfs(buf, len))
  244. return false;
  245. return true;
  246. }
  247. //// Mac OS X, Darwin
  248. #elif defined(__APPLE__)
  249. bool getCurrentExecPath(char *buf, size_t len)
  250. {
  251. uint32_t lenb = (uint32_t)len;
  252. if (_NSGetExecutablePath(buf, &lenb) == -1)
  253. return false;
  254. return true;
  255. }
  256. //// FreeBSD, NetBSD, DragonFlyBSD
  257. #elif defined(__FreeBSD__) || defined(__NetBSD__) || defined(__DragonFly__)
  258. bool getCurrentExecPath(char *buf, size_t len)
  259. {
  260. // Try getting path from procfs first, since valgrind
  261. // doesn't work with the latter
  262. if (getExecPathFromProcfs(buf, len))
  263. return true;
  264. int mib[4];
  265. mib[0] = CTL_KERN;
  266. mib[1] = KERN_PROC;
  267. mib[2] = KERN_PROC_PATHNAME;
  268. mib[3] = -1;
  269. if (sysctl(mib, 4, buf, &len, NULL, 0) == -1)
  270. return false;
  271. return true;
  272. }
  273. #elif defined(__HAIKU__)
  274. bool getCurrentExecPath(char *buf, size_t len)
  275. {
  276. return find_path(B_APP_IMAGE_SYMBOL, B_FIND_PATH_IMAGE_PATH, NULL, buf, len) == B_OK;
  277. }
  278. //// Solaris
  279. #elif defined(__sun) || defined(sun)
  280. bool getCurrentExecPath(char *buf, size_t len)
  281. {
  282. const char *exec = getexecname();
  283. if (exec == NULL)
  284. return false;
  285. if (strlcpy(buf, exec, len) >= len)
  286. return false;
  287. return true;
  288. }
  289. // HP-UX
  290. #elif defined(__hpux)
  291. bool getCurrentExecPath(char *buf, size_t len)
  292. {
  293. struct pst_status psts;
  294. if (pstat_getproc(&psts, sizeof(psts), 0, getpid()) == -1)
  295. return false;
  296. if (pstat_getpathname(buf, len, &psts.pst_fid_text) == -1)
  297. return false;
  298. return true;
  299. }
  300. #else
  301. bool getCurrentExecPath(char *buf, size_t len)
  302. {
  303. return false;
  304. }
  305. #endif
  306. //// Non-Windows
  307. #if !defined(_WIN32)
  308. const char *getHomeOrFail()
  309. {
  310. const char *home = getenv("HOME");
  311. // In rare cases the HOME environment variable may be unset
  312. FATAL_ERROR_IF(!home,
  313. "Required environment variable HOME is not set");
  314. return home;
  315. }
  316. #endif
  317. //// Windows
  318. #if defined(_WIN32)
  319. bool setSystemPaths()
  320. {
  321. char buf[BUFSIZ];
  322. // Find path of executable and set path_share relative to it
  323. FATAL_ERROR_IF(!getCurrentExecPath(buf, sizeof(buf)),
  324. "Failed to get current executable path");
  325. pathRemoveFile(buf, '\\');
  326. std::string exepath(buf);
  327. // Use ".\bin\.."
  328. path_share = exepath + "\\..";
  329. if (detectMSVCBuildDir(exepath)) {
  330. // The msvc build dir schould normaly not be present if properly installed,
  331. // but its useful for debugging.
  332. path_share += DIR_DELIM "..";
  333. }
  334. // Use %MINETEST_USER_PATH%
  335. DWORD len = GetEnvironmentVariable("MINETEST_USER_PATH", buf, sizeof(buf));
  336. FATAL_ERROR_IF(len > sizeof(buf), "Failed to get MINETEST_USER_PATH (too large for buffer)");
  337. if (len == 0) {
  338. // Use "C:\Users\<user>\AppData\Roaming\<PROJECT_NAME_C>"
  339. len = GetEnvironmentVariable("APPDATA", buf, sizeof(buf));
  340. FATAL_ERROR_IF(len == 0 || len > sizeof(buf), "Failed to get APPDATA");
  341. path_user = std::string(buf) + DIR_DELIM + PROJECT_NAME_C;
  342. } else {
  343. path_user = std::string(buf);
  344. }
  345. return true;
  346. }
  347. //// Linux
  348. #elif defined(__linux__) || defined(__FreeBSD__) || defined(__NetBSD__) || defined(__DragonFly__)
  349. bool setSystemPaths()
  350. {
  351. char buf[BUFSIZ];
  352. if (!getCurrentExecPath(buf, sizeof(buf))) {
  353. #ifdef __ANDROID__
  354. errorstream << "Unable to read bindir "<< std::endl;
  355. #else
  356. FATAL_ERROR("Unable to read bindir");
  357. #endif
  358. return false;
  359. }
  360. pathRemoveFile(buf, '/');
  361. std::string bindir(buf);
  362. // Find share directory from these.
  363. // It is identified by containing the subdirectory "builtin".
  364. std::list<std::string> trylist;
  365. std::string static_sharedir = STATIC_SHAREDIR;
  366. if (!static_sharedir.empty() && static_sharedir != ".")
  367. trylist.push_back(static_sharedir);
  368. trylist.push_back(bindir + DIR_DELIM ".." DIR_DELIM "share"
  369. DIR_DELIM + PROJECT_NAME);
  370. trylist.push_back(bindir + DIR_DELIM "..");
  371. #ifdef __ANDROID__
  372. trylist.push_back(path_user);
  373. #endif
  374. for (std::list<std::string>::const_iterator
  375. i = trylist.begin(); i != trylist.end(); ++i) {
  376. const std::string &trypath = *i;
  377. if (!fs::PathExists(trypath) ||
  378. !fs::PathExists(trypath + DIR_DELIM + "builtin")) {
  379. warningstream << "system-wide share not found at \""
  380. << trypath << "\""<< std::endl;
  381. continue;
  382. }
  383. // Warn if was not the first alternative
  384. if (i != trylist.begin()) {
  385. warningstream << "system-wide share found at \""
  386. << trypath << "\"" << std::endl;
  387. }
  388. path_share = trypath;
  389. break;
  390. }
  391. #ifndef __ANDROID__
  392. const char *const minetest_user_path = getenv("MINETEST_USER_PATH");
  393. if (minetest_user_path && minetest_user_path[0] != '\0') {
  394. path_user = std::string(minetest_user_path);
  395. } else {
  396. path_user = std::string(getHomeOrFail()) + DIR_DELIM "."
  397. + PROJECT_NAME;
  398. }
  399. #endif
  400. return true;
  401. }
  402. //// Mac OS X
  403. #elif defined(__APPLE__)
  404. bool setSystemPaths()
  405. {
  406. CFBundleRef main_bundle = CFBundleGetMainBundle();
  407. CFURLRef resources_url = CFBundleCopyResourcesDirectoryURL(main_bundle);
  408. char path[PATH_MAX];
  409. if (CFURLGetFileSystemRepresentation(resources_url,
  410. TRUE, (UInt8 *)path, PATH_MAX)) {
  411. path_share = std::string(path);
  412. } else {
  413. warningstream << "Could not determine bundle resource path" << std::endl;
  414. }
  415. CFRelease(resources_url);
  416. const char *const minetest_user_path = getenv("MINETEST_USER_PATH");
  417. if (minetest_user_path && minetest_user_path[0] != '\0') {
  418. path_user = std::string(minetest_user_path);
  419. } else {
  420. path_user = std::string(getHomeOrFail())
  421. + "/Library/Application Support/"
  422. + PROJECT_NAME;
  423. }
  424. return true;
  425. }
  426. #else
  427. bool setSystemPaths()
  428. {
  429. path_share = STATIC_SHAREDIR;
  430. const char *const minetest_user_path = getenv("MINETEST_USER_PATH");
  431. if (minetest_user_path && minetest_user_path[0] != '\0') {
  432. path_user = std::string(minetest_user_path);
  433. } else {
  434. path_user = std::string(getHomeOrFail()) + DIR_DELIM "."
  435. + lowercase(PROJECT_NAME);
  436. }
  437. return true;
  438. }
  439. #endif
  440. void migrateCachePath()
  441. {
  442. const std::string local_cache_path = path_user + DIR_DELIM + "cache";
  443. // Delete tmp folder if it exists (it only ever contained
  444. // a temporary ogg file, which is no longer used).
  445. if (fs::PathExists(local_cache_path + DIR_DELIM + "tmp"))
  446. fs::RecursiveDelete(local_cache_path + DIR_DELIM + "tmp");
  447. // Bail if migration impossible
  448. if (path_cache == local_cache_path || !fs::PathExists(local_cache_path)
  449. || fs::PathExists(path_cache)) {
  450. return;
  451. }
  452. if (!fs::Rename(local_cache_path, path_cache)) {
  453. errorstream << "Failed to migrate local cache path "
  454. "to system path!" << std::endl;
  455. }
  456. }
  457. void initializePaths()
  458. {
  459. #if RUN_IN_PLACE
  460. char buf[BUFSIZ];
  461. infostream << "Using relative paths (RUN_IN_PLACE)" << std::endl;
  462. bool success =
  463. getCurrentExecPath(buf, sizeof(buf)) ||
  464. getExecPathFromProcfs(buf, sizeof(buf));
  465. if (success) {
  466. pathRemoveFile(buf, DIR_DELIM_CHAR);
  467. std::string execpath(buf);
  468. path_share = execpath + DIR_DELIM "..";
  469. path_user = execpath + DIR_DELIM "..";
  470. if (detectMSVCBuildDir(execpath)) {
  471. path_share += DIR_DELIM "..";
  472. path_user += DIR_DELIM "..";
  473. }
  474. } else {
  475. errorstream << "Failed to get paths by executable location, "
  476. "trying cwd" << std::endl;
  477. if (!getCurrentWorkingDir(buf, sizeof(buf)))
  478. FATAL_ERROR("Ran out of methods to get paths");
  479. size_t cwdlen = strlen(buf);
  480. if (cwdlen >= 1 && buf[cwdlen - 1] == DIR_DELIM_CHAR) {
  481. cwdlen--;
  482. buf[cwdlen] = '\0';
  483. }
  484. if (cwdlen >= 4 && !strcmp(buf + cwdlen - 4, DIR_DELIM "bin"))
  485. pathRemoveFile(buf, DIR_DELIM_CHAR);
  486. std::string execpath(buf);
  487. path_share = execpath;
  488. path_user = execpath;
  489. }
  490. path_cache = path_user + DIR_DELIM + "cache";
  491. #else
  492. infostream << "Using system-wide paths (NOT RUN_IN_PLACE)" << std::endl;
  493. if (!setSystemPaths())
  494. errorstream << "Failed to get one or more system-wide path" << std::endl;
  495. # ifdef _WIN32
  496. path_cache = path_user + DIR_DELIM + "cache";
  497. # else
  498. // Initialize path_cache
  499. // First try $XDG_CACHE_HOME/PROJECT_NAME
  500. const char *cache_dir = getenv("XDG_CACHE_HOME");
  501. const char *home_dir = getenv("HOME");
  502. if (cache_dir && cache_dir[0] != '\0') {
  503. path_cache = std::string(cache_dir) + DIR_DELIM + PROJECT_NAME;
  504. } else if (home_dir) {
  505. // Then try $HOME/.cache/PROJECT_NAME
  506. path_cache = std::string(home_dir) + DIR_DELIM + ".cache"
  507. + DIR_DELIM + PROJECT_NAME;
  508. } else {
  509. // If neither works, use $PATH_USER/cache
  510. path_cache = path_user + DIR_DELIM + "cache";
  511. }
  512. // Migrate cache folder to new location if possible
  513. migrateCachePath();
  514. # endif // _WIN32
  515. #endif // RUN_IN_PLACE
  516. infostream << "Detected share path: " << path_share << std::endl;
  517. infostream << "Detected user path: " << path_user << std::endl;
  518. infostream << "Detected cache path: " << path_cache << std::endl;
  519. #if USE_GETTEXT
  520. bool found_localedir = false;
  521. # ifdef STATIC_LOCALEDIR
  522. /* STATIC_LOCALEDIR may be a generalized path such as /usr/share/locale that
  523. * doesn't necessarily contain our locale files, so check data path first. */
  524. path_locale = getDataPath("locale");
  525. if (fs::PathExists(path_locale)) {
  526. found_localedir = true;
  527. infostream << "Using in-place locale directory " << path_locale
  528. << " even though a static one was provided." << std::endl;
  529. } else if (STATIC_LOCALEDIR[0] && fs::PathExists(STATIC_LOCALEDIR)) {
  530. found_localedir = true;
  531. path_locale = STATIC_LOCALEDIR;
  532. infostream << "Using static locale directory " << STATIC_LOCALEDIR
  533. << std::endl;
  534. }
  535. # else
  536. path_locale = getDataPath("locale");
  537. if (fs::PathExists(path_locale)) {
  538. found_localedir = true;
  539. }
  540. # endif
  541. if (!found_localedir) {
  542. warningstream << "Couldn't find a locale directory!" << std::endl;
  543. }
  544. #endif // USE_GETTEXT
  545. }
  546. ////
  547. //// OS-specific Secure Random
  548. ////
  549. #ifdef WIN32
  550. bool secure_rand_fill_buf(void *buf, size_t len)
  551. {
  552. HCRYPTPROV wctx;
  553. if (!CryptAcquireContext(&wctx, NULL, NULL, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT))
  554. return false;
  555. CryptGenRandom(wctx, len, (BYTE *)buf);
  556. CryptReleaseContext(wctx, 0);
  557. return true;
  558. }
  559. #else
  560. bool secure_rand_fill_buf(void *buf, size_t len)
  561. {
  562. // N.B. This function checks *only* for /dev/urandom, because on most
  563. // common OSes it is non-blocking, whereas /dev/random is blocking, and it
  564. // is exceptionally uncommon for there to be a situation where /dev/random
  565. // exists but /dev/urandom does not. This guesswork is necessary since
  566. // random devices are not covered by any POSIX standard...
  567. FILE *fp = fopen("/dev/urandom", "rb");
  568. if (!fp)
  569. return false;
  570. bool success = fread(buf, len, 1, fp) == 1;
  571. fclose(fp);
  572. return success;
  573. }
  574. #endif
  575. void attachOrCreateConsole()
  576. {
  577. #ifdef _WIN32
  578. static bool consoleAllocated = false;
  579. const bool redirected = (_fileno(stdout) == -2 || _fileno(stdout) == -1); // If output is redirected to e.g a file
  580. if (!consoleAllocated && redirected && (AttachConsole(ATTACH_PARENT_PROCESS) || AllocConsole())) {
  581. freopen("CONOUT$", "w", stdout);
  582. freopen("CONOUT$", "w", stderr);
  583. consoleAllocated = true;
  584. }
  585. #endif
  586. }
  587. #ifdef _WIN32
  588. std::string QuoteArgv(const std::string &arg)
  589. {
  590. // Quoting rules on Windows are batshit insane, can differ between applications
  591. // and there isn't even a stdlib function to deal with it.
  592. // Ref: https://learn.microsoft.com/archive/blogs/twistylittlepassagesallalike/everyone-quotes-command-line-arguments-the-wrong-way
  593. if (!arg.empty() && arg.find_first_of(" \t\n\v\"") == std::string::npos)
  594. return arg;
  595. std::string ret;
  596. ret.reserve(arg.size()+2);
  597. ret.push_back('"');
  598. for (auto it = arg.begin(); it != arg.end(); ++it) {
  599. u32 back = 0;
  600. while (it != arg.end() && *it == '\\')
  601. ++back, ++it;
  602. if (it == arg.end()) {
  603. ret.append(2 * back, '\\');
  604. break;
  605. } else if (*it == '"') {
  606. ret.append(2 * back + 1, '\\');
  607. } else {
  608. ret.append(back, '\\');
  609. }
  610. ret.push_back(*it);
  611. }
  612. ret.push_back('"');
  613. return ret;
  614. }
  615. #endif
  616. int mt_snprintf(char *buf, const size_t buf_size, const char *fmt, ...)
  617. {
  618. // https://msdn.microsoft.com/en-us/library/bt7tawza.aspx
  619. // Many of the MSVC / Windows printf-style functions do not support positional
  620. // arguments (eg. "%1$s"). We just forward the call to vsnprintf for sane
  621. // platforms, but defer to _vsprintf_p on MSVC / Windows.
  622. // https://github.com/FFmpeg/FFmpeg/blob/5ae9fa13f5ac640bec113120d540f70971aa635d/compat/msvcrt/snprintf.c#L46
  623. // _vsprintf_p has to be shimmed with _vscprintf_p on -1 (for an example see
  624. // above FFmpeg link).
  625. va_list args;
  626. va_start(args, fmt);
  627. #ifndef _MSC_VER
  628. int c = vsnprintf(buf, buf_size, fmt, args);
  629. #else // _MSC_VER
  630. int c = _vsprintf_p(buf, buf_size, fmt, args);
  631. if (c == -1)
  632. c = _vscprintf_p(fmt, args);
  633. #endif // _MSC_VER
  634. va_end(args);
  635. return c;
  636. }
  637. static bool open_uri(const std::string &uri)
  638. {
  639. if (uri.find_first_of("\r\n") != std::string::npos) {
  640. errorstream << "Unable to open URI as it is invalid, contains new line: " << uri << std::endl;
  641. return false;
  642. }
  643. #if defined(_WIN32)
  644. return (intptr_t)ShellExecuteA(NULL, NULL, uri.c_str(), NULL, NULL, SW_SHOWNORMAL) > 32;
  645. #elif defined(__ANDROID__)
  646. openURIAndroid(uri);
  647. return true;
  648. #elif defined(__APPLE__)
  649. const char *argv[] = {"open", uri.c_str(), NULL};
  650. return posix_spawnp(NULL, "open", NULL, NULL, (char**)argv,
  651. (*_NSGetEnviron())) == 0;
  652. #else
  653. const char *argv[] = {"xdg-open", uri.c_str(), NULL};
  654. return posix_spawnp(NULL, "xdg-open", NULL, NULL, (char**)argv, environ) == 0;
  655. #endif
  656. }
  657. bool open_url(const std::string &url)
  658. {
  659. if (url.substr(0, 7) != "http://" && url.substr(0, 8) != "https://") {
  660. errorstream << "Unable to open browser as URL is missing schema: " << url << std::endl;
  661. return false;
  662. }
  663. return open_uri(url);
  664. }
  665. bool open_directory(const std::string &path)
  666. {
  667. if (!fs::IsDir(path)) {
  668. errorstream << "Unable to open directory as it does not exist: " << path << std::endl;
  669. return false;
  670. }
  671. return open_uri(path);
  672. }
  673. // Load performance counter frequency only once at startup
  674. #ifdef _WIN32
  675. inline double get_perf_freq()
  676. {
  677. // Also use this opportunity to enable high-res timers
  678. timeBeginPeriod(1);
  679. LARGE_INTEGER freq;
  680. QueryPerformanceFrequency(&freq);
  681. return freq.QuadPart;
  682. }
  683. double perf_freq = get_perf_freq();
  684. #endif
  685. } //namespace porting