EAProcess.cpp 31 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075
  1. ///////////////////////////////////////////////////////////////////////////////
  2. // Copyright (c) Electronic Arts Inc. All rights reserved.
  3. ///////////////////////////////////////////////////////////////////////////////
  4. ///////////////////////////////////////////////////////////////////////////////
  5. // This module defines functions for process spawning and query.
  6. /////////////////////////////////////////////////////////////////////////////
  7. #include <EAStdC/internal/Config.h>
  8. #include <EAStdC/EAProcess.h>
  9. #include <EAStdC/EAString.h>
  10. #include <string.h>
  11. #include <EAAssert/eaassert.h>
  12. #if defined(EA_PLATFORM_MICROSOFT)
  13. #pragma warning(push, 0)
  14. #include <Windows.h>
  15. #include <stdlib.h>
  16. #include <process.h>
  17. #if EA_WINAPI_FAMILY_PARTITION(EA_WINAPI_PARTITION_DESKTOP)
  18. #include <ShellAPI.h>
  19. #pragma warning(pop)
  20. #ifdef _MSC_VER
  21. #pragma comment(lib, "shell32.lib") // Required for shellapi calls.
  22. #endif
  23. #endif
  24. #elif defined(EA_PLATFORM_UNIX)
  25. #include <sys/types.h>
  26. #if EASTDC_SYS_WAIT_H_AVAILABLE
  27. #include <sys/wait.h>
  28. #endif
  29. #include <unistd.h>
  30. #include <errno.h>
  31. #include <stdio.h>
  32. #elif defined(EA_PLATFORM_SONY) && EA_SCEDBG_ENABLED
  33. #include <libdbg.h>
  34. #endif
  35. #if defined(EA_PLATFORM_APPLE)
  36. #include <mach-o/dyld.h> // _NSGetExecutablePath
  37. #include <sys/syslimits.h> // PATH_MAX
  38. #include <libgen.h> // dirname
  39. namespace EA
  40. {
  41. namespace StdC
  42. {
  43. //Used to determine if a given path is a bundle extension
  44. const char8_t* kBundleExtensions[] = {
  45. ".app",
  46. ".bundle",
  47. ".plugin"
  48. };
  49. }
  50. }
  51. #endif
  52. namespace EA
  53. {
  54. namespace StdC
  55. {
  56. // EASTDC_SETCURRENTPROCESSPATH_REQUIRED
  57. //
  58. // Defined as 0 or 1.
  59. //
  60. #ifndef EASTDC_SETCURRENTPROCESSPATH_REQUIRED
  61. #if defined(EA_PLATFORM_SONY) && defined(EA_PLATFORM_CONSOLE)
  62. #define EASTDC_SETCURRENTPROCESSPATH_REQUIRED 1
  63. #else
  64. #define EASTDC_SETCURRENTPROCESSPATH_REQUIRED 0
  65. #endif
  66. #endif
  67. #if EASTDC_SETCURRENTPROCESSPATH_REQUIRED
  68. static char8_t gCurrentProcessPath[kMaxPathLength] = { 0 };
  69. #endif
  70. EASTDC_API void SetCurrentProcessPath(const char8_t* pPath)
  71. {
  72. #if EASTDC_SETCURRENTPROCESSPATH_REQUIRED
  73. Strlcpy(gCurrentProcessPath, pPath, EAArrayCount(gCurrentProcessPath));
  74. #else
  75. EA_UNUSED(pPath);
  76. #endif
  77. }
  78. #if defined(EA_PLATFORM_MICROSOFT) && EA_WINAPI_FAMILY_PARTITION(EA_WINAPI_PARTITION_DESKTOP | EA_WINAPI_PARTITION_TV_APP | EA_WINAPI_PARTITION_TV_TITLE | EA_WINAPI_PARTITION_GAMES)
  79. // According to Microsoft documentation:
  80. // The GetModuleFileName function retrieves the full path and file name
  81. // for the file containing the specified module.
  82. // If the function succeeds, the return value is the length of the string
  83. // copied to the buffer, in TCHARs. If the buffer is too small to hold the
  84. // module name, the string is truncated to the user-supplied capacity, and
  85. // the function returns that capacity.
  86. EASTDC_API size_t GetCurrentProcessPath(char16_t* pPath, int pathCapacity, int /*pathFlags*/)
  87. {
  88. EA_ASSERT(pathCapacity > 0); EA_UNUSED(pathCapacity);
  89. const DWORD dwResult = GetModuleFileNameW(NULL, reinterpret_cast<LPWSTR>(pPath), (DWORD)pathCapacity);
  90. if((dwResult != 0) && (dwResult < (DWORD)pathCapacity)) // If there wasn't an error and there was enough capacity...
  91. return (size_t)dwResult;
  92. pPath[0] = 0;
  93. return 0;
  94. }
  95. EASTDC_API size_t GetCurrentProcessPath(char8_t* pPath, int pathCapacity, int pathFlags)
  96. {
  97. EA_ASSERT(pathCapacity > 0); EA_UNUSED(pathCapacity);
  98. // We cannot use GetModuleFileNameA here, because the text encoding of
  99. // GetModuleFileNameA is arbitrary and in any case is usually not UTF8.
  100. char16_t path16[kMaxPathLength];
  101. GetCurrentProcessPath(path16, EAArrayCount(path16), pathFlags);
  102. const int intendedStrlen = Strlcpy(pPath, path16, (size_t)pathCapacity);
  103. if((intendedStrlen >= 0) && (intendedStrlen < pathCapacity))
  104. return (size_t)intendedStrlen;
  105. pPath[0] = 0;
  106. return 0;
  107. }
  108. EASTDC_API size_t GetCurrentProcessDirectory(char16_t* pDirectory, int pathCapacity, int /*pathFlags*/)
  109. {
  110. EA_ASSERT(pathCapacity > 0); EA_UNUSED(pathCapacity);
  111. const DWORD dwResult = GetModuleFileNameW(NULL, reinterpret_cast<LPWSTR>(pDirectory), (DWORD)pathCapacity);
  112. if((dwResult != 0) && (dwResult < (DWORD)pathCapacity)) // If there wasn't an error and there was enough capacity...
  113. {
  114. DWORD dw;
  115. for(dw = dwResult; dw > 0; --dw)
  116. {
  117. if((pDirectory[dw - 1] != '/') && (pDirectory[dw - 1] != '\\'))
  118. pDirectory[dw - 1] = 0;
  119. else
  120. break;
  121. }
  122. return dw;
  123. }
  124. pDirectory[0] = 0;
  125. return 0;
  126. }
  127. EASTDC_API size_t GetCurrentProcessDirectory(char8_t* pDirectory, int pathCapacity, int pathFlags)
  128. {
  129. EA_ASSERT(pathCapacity > 0); EA_UNUSED(pathCapacity);
  130. char16_t path16[kMaxDirectoryLength];
  131. GetCurrentProcessDirectory(path16, EAArrayCount(path16), pathFlags);
  132. const int intendedStrlen = Strlcpy(pDirectory, path16, (size_t)pathCapacity);
  133. if((intendedStrlen >= 0) && (intendedStrlen < pathCapacity))
  134. return (size_t)intendedStrlen;
  135. pDirectory[0] = 0;
  136. return 0;
  137. }
  138. #elif defined(EA_PLATFORM_SONY) && EA_SCEDBG_ENABLED // Debug time only, as the following code would not be allowed for use in retail kits.
  139. // sceDbgGetExecutablePath
  140. // Gets the application file path. The path can only be obtained for applications run
  141. // from the host PC (run by Visual Studio, Neighborhood or the -run command).
  142. EASTDC_API size_t GetCurrentProcessPath(char8_t* pPath, int pathCapacity, int /*pathFlags*/)
  143. {
  144. EA_ASSERT(pathCapacity > 0);
  145. size_t result;
  146. // If the user has set the process path, use the setting instead of querying.
  147. if (gCurrentProcessPath[0])
  148. {
  149. result = Strlcpy(pPath, gCurrentProcessPath, pathCapacity);
  150. }
  151. else
  152. {
  153. result = (size_t)sceDbgGetExecutablePath(pPath, (size_t)(unsigned)pathCapacity);
  154. }
  155. if(result < (size_t)pathCapacity) // sceDbgGetExecutablePath returns the requires strlen.
  156. {
  157. return result;
  158. }
  159. else
  160. {
  161. pPath[0] = 0;
  162. return 0; // sceDbgGetExecutablePath always 0-terminates, even on too small capacity.
  163. }
  164. }
  165. EASTDC_API size_t GetCurrentProcessPath(char16_t* pPath, int pathCapacity, int pathFlags)
  166. {
  167. EA_ASSERT(pathCapacity > 0);
  168. char8_t path8[kMaxPathLength];
  169. GetCurrentProcessPath(path8, EAArrayCount(path8), pathFlags);
  170. const int intendedStrlen = Strlcpy(pPath, path8, pathCapacity);
  171. if((intendedStrlen >= 0) && (intendedStrlen < pathCapacity)) // If successful (UTF8 to UTF16 conversions can theoretically fail, if the encoded chars are bad...
  172. return (size_t)intendedStrlen;
  173. pPath[0] = 0;
  174. return 0;
  175. }
  176. EASTDC_API size_t GetCurrentProcessDirectory(char8_t* pDirectory, int pathCapacity, int /*pathFlags*/)
  177. {
  178. int32_t result;
  179. if (gCurrentProcessPath[0])
  180. {
  181. result = (int32_t)Strlcpy(pDirectory, gCurrentProcessPath, pathCapacity);
  182. }
  183. else
  184. {
  185. result = sceDbgGetExecutablePath(pDirectory, (size_t)(unsigned)pathCapacity);
  186. }
  187. if(result < pathCapacity) // sceDbgGetExecutablePath returns the requires strlen.
  188. {
  189. while (result > 0)
  190. {
  191. if (pDirectory[result - 1] == '/' || pDirectory[result - 1] == '\\')
  192. break;
  193. pDirectory[--result] = '\0';
  194. }
  195. return (size_t)(uint32_t)(result);
  196. }
  197. else
  198. {
  199. pDirectory[0] = 0;
  200. return 0; // sceDbgGetExecutablePath always 0-terminates, even on too small capacity.
  201. }
  202. }
  203. EASTDC_API size_t GetCurrentProcessDirectory(char16_t* pDirectory, int pathCapacity, int pathFlags)
  204. {
  205. EA_ASSERT(pathCapacity > 0);
  206. char8_t path8[kMaxDirectoryLength]; // We don't have access to EAIO here.
  207. GetCurrentProcessDirectory(path8, EAArrayCount(path8), pathFlags);
  208. const int intendedStrlen = Strlcpy(pDirectory, path8, pathCapacity);
  209. if((intendedStrlen >= 0) && (intendedStrlen < pathCapacity)) // If successful (UTF8 to UTF16 conversions can theoretically fail, if the encoded chars are bad...
  210. return (size_t)intendedStrlen;
  211. pDirectory[0] = 0;
  212. return 0;
  213. }
  214. #elif defined(EA_PLATFORM_APPLE)
  215. EASTDC_API size_t GetCurrentProcessPath(char16_t* pPath, int pathCapacity, int pathFlags)
  216. {
  217. EA_ASSERT(pathCapacity > 0); EA_UNUSED(pathCapacity);
  218. char8_t path8[kMaxPathLength];
  219. GetCurrentProcessPath(path8, EAArrayCount(path8), pathFlags);
  220. const int intendedStrlen = Strlcpy(pPath, path8, pathCapacity);
  221. if((intendedStrlen >= 0) && (intendedStrlen < pathCapacity)) // If successful (UTF8 to UTF16 conversions can theoretically fail, if the encoded chars are bad...
  222. return (size_t)intendedStrlen;
  223. pPath[0] = 0;
  224. return 0;
  225. }
  226. static bool IsBundleFolder(char8_t* pPath, int pathCapacity)
  227. {
  228. for(size_t i = 0; i < EAArrayCount(kBundleExtensions); i++)
  229. {
  230. if(Striend(pPath, kBundleExtensions[i]))
  231. {
  232. return true;
  233. }
  234. }
  235. return false;
  236. }
  237. // To consider: add a flag so user can specify if they want the path to the actual executable even if it is in a .extension
  238. // EG: /path/to/MyApp.extension or /path/to/MyApp.extension/MyExecutable
  239. // Currently /path/to/.extension is returned if it exists, otherwise it returns the executable path
  240. EASTDC_API size_t GetCurrentProcessPath(char8_t* pPath, int pathCapacity, int pathFlags)
  241. {
  242. EA_ASSERT(pathCapacity > 0);
  243. // https://developer.apple.com/library/mac/#documentation/Darwin/Reference/ManPages/man3/dyld.3.html
  244. // http://lists.apple.com/archives/darwin-dev/2008/Dec/msg00037.html
  245. uint32_t capacityU32 = (uint32_t)pathCapacity;
  246. int result = _NSGetExecutablePath(pPath, &capacityU32); // Returns -1 and sets capacityU32 if the capacity is not enough.
  247. if(result == 0)
  248. {
  249. EA_ASSERT(pathCapacity >= kMaxPathLength);
  250. char8_t absolutePath[PATH_MAX];
  251. if(realpath(pPath, absolutePath) != NULL) // Obtain canonicalized absolute pathname.
  252. {
  253. if(pathFlags & kPathFlagBundlePath)
  254. {
  255. // We recursively call dirname() until we find .extension
  256. char8_t appPath[kMaxPathLength];
  257. EA::StdC::Strlcpy(appPath, absolutePath, kMaxPathLength);
  258. bool found = IsBundleFolder(appPath, kMaxPathLength);
  259. while(!found &&
  260. EA::StdC::Strncmp(appPath, ".", kMaxPathLength) != 0 &&
  261. EA::StdC::Strncmp(appPath, "/", kMaxPathLength) != 0)
  262. {
  263. EA::StdC::Strlcpy(appPath,dirname(appPath), kMaxPathLength);
  264. found = IsBundleFolder(appPath, kMaxPathLength);
  265. }
  266. if(found)
  267. EA::StdC::Strlcpy(pPath, appPath, pathCapacity);
  268. else // If not found, we use the original executable absolute path.
  269. EA::StdC::Strlcpy(pPath, absolutePath, pathCapacity);
  270. }
  271. else
  272. EA::StdC::Strlcpy(pPath, absolutePath, pathCapacity);
  273. return Strlen(pPath);
  274. }
  275. }
  276. pPath[0] = 0;
  277. return 0;
  278. }
  279. EASTDC_API size_t GetCurrentProcessDirectory(char16_t* pDirectory, int pathCapacity, int pathFlags)
  280. {
  281. EA_ASSERT(pathCapacity > 0); EA_UNUSED(pathCapacity);
  282. char8_t path8[kMaxDirectoryLength]; // We don't have access to EAIO here.
  283. GetCurrentProcessDirectory(path8, EAArrayCount(path8), pathFlags);
  284. const int intendedStrlen = Strlcpy(pDirectory, path8, pathCapacity);
  285. if((intendedStrlen >= 0) && (intendedStrlen < pathCapacity)) // If successful (UTF8 to UTF16 conversions can theoretically fail, if the encoded chars are bad...
  286. return (size_t)intendedStrlen;
  287. pDirectory[0] = 0;
  288. return 0;
  289. }
  290. EASTDC_API size_t GetCurrentProcessDirectory(char8_t* pDirectory, int pathCapacity, int pathFlags)
  291. {
  292. EA_ASSERT(pathCapacity > 0);
  293. char pAppPath[kMaxPathLength];
  294. size_t n = GetCurrentProcessPath(pAppPath, kMaxPathLength, pathFlags);
  295. if(n > 0)
  296. {
  297. // argv[0] pDirectory
  298. // --------------------------------------------------
  299. // "" -> "" (Should never happen)
  300. // "/" -> "/" (Should never happen)
  301. // "a" -> "a" (Should never happen)
  302. // "/a/b" -> /a/"
  303. EA_COMPILETIME_ASSERT(kMaxDirectoryLength >= kMaxPathLength); // We assert this because argv[0] could concievably be as long as kMaxPathLength.
  304. const size_t intendedStrlen = Strlcpy(pDirectory, pAppPath, pathCapacity);
  305. if(intendedStrlen < (size_t)pathCapacity) // If succeeded...
  306. {
  307. for(char8_t* p = pDirectory + intendedStrlen; p > pDirectory; --p)
  308. {
  309. if(p[-1] == '/')
  310. {
  311. p[0] = 0; // e.g. /aaa/bbb/ccc => /aaa/bbb/
  312. return (size_t)(p - pDirectory);
  313. }
  314. }
  315. // Alternative implementation which we should validate, as it's simpler:
  316. //char* p = strrchr(pDirectory, '/');
  317. //if(p) // This should usually (always?) be valid.
  318. // *p = 0; // e.g. /aaa/bbb/ccc => /aaa/bbb/
  319. return Strlen(pDirectory);
  320. }
  321. }
  322. pDirectory[0] = 0;
  323. return 0;
  324. }
  325. #elif defined(EA_PLATFORM_LINUX)
  326. EASTDC_API size_t GetCurrentProcessPath(char8_t* pPath, int pathCapacity, int /*pathFlags*/)
  327. {
  328. EA_ASSERT(pathCapacity > 0); EA_UNUSED(pathCapacity);
  329. ssize_t resultLen = readlink("/proc/self/exe", pPath, pathCapacity);
  330. if( resultLen != -1 )
  331. {
  332. ssize_t terminatorLocation = resultLen < (pathCapacity-1) ? resultLen : (pathCapacity-1);
  333. pPath[terminatorLocation] = '\0';
  334. return terminatorLocation;
  335. }
  336. else
  337. {
  338. pPath[0] = 0;
  339. return 0;
  340. }
  341. }
  342. EASTDC_API size_t GetCurrentProcessPath(char16_t* pPath, int pathCapacity, int pathFlags)
  343. {
  344. EA_ASSERT(pathCapacity > 0); EA_UNUSED(pathCapacity);
  345. char8_t path8[kMaxPathLength];
  346. GetCurrentProcessPath(path8, EAArrayCount(path8), pathFlags);
  347. const int intendedStrlen = Strlcpy(pPath, path8, pathCapacity);
  348. if((intendedStrlen >= 0) && (intendedStrlen < pathCapacity)) // If successful (UTF8 to UTF16 conversions can theoretically fail, if the encoded chars are bad...
  349. return (size_t)intendedStrlen;
  350. pPath[0] = 0;
  351. return 0;
  352. }
  353. EASTDC_API size_t GetCurrentProcessDirectory(char8_t* pDirectory, int pathCapacity, int /*pathFlags*/)
  354. {
  355. EA_ASSERT(pathCapacity > 0); EA_UNUSED(pathCapacity);
  356. ssize_t resultLen = readlink("/proc/self/exe", pDirectory, pathCapacity);
  357. if( resultLen != -1 )
  358. {
  359. for(int pos = resultLen; pos > 0; --pos)
  360. {
  361. if(pDirectory[pos - 1] != '/')
  362. pDirectory[pos - 1] = 0;
  363. else
  364. break;
  365. }
  366. return strlen(pDirectory);
  367. }
  368. else
  369. {
  370. pDirectory[0] = 0;
  371. return 0;
  372. }
  373. }
  374. EASTDC_API size_t GetCurrentProcessDirectory(char16_t* pDirectory, int pathCapacity, int pathFlags)
  375. {
  376. EA_ASSERT(pathCapacity > 0); EA_UNUSED(pathCapacity);
  377. char8_t path8[kMaxDirectoryLength]; // We don't have access to EAIO here.
  378. GetCurrentProcessDirectory(path8, EAArrayCount(path8), pathFlags);
  379. const int intendedStrlen = Strlcpy(pDirectory, path8, pathCapacity);
  380. if((intendedStrlen >= 0) && (intendedStrlen < pathCapacity)) // If successful (UTF8 to UTF16 conversions can theoretically fail, if the encoded chars are bad...
  381. return (size_t)intendedStrlen;
  382. pDirectory[0] = 0;
  383. return 0;
  384. }
  385. #if 0
  386. /*
  387. // http://blog.linuxgamepublishing.com/2009/10/12/argv-and-argc-and-just-how-to-get-them/
  388. // http://stackoverflow.com/questions/1585989/how-to-parse-proc-pid-cmdline
  389. // https://www.google.com/search?q=%2Fproc%2Fself%2Fcmdline
  390. char** get_argv()
  391. {
  392. static char emptyNonConstString[1][1] = { { 0 } };
  393. static char** savedArgv = NULL;
  394. if(!savedArgv)
  395. {
  396. FILE* pFile = fopen("/proc/self/cmdline", "r");
  397. if(pFile) // This should be true for at least all recent Linux versions.
  398. {
  399. const size_t kBufferSize = 1024; // This should be dynamically allocated if we are to be able to ready any buffer.
  400. char buffer[kBufferSize];
  401. size_t count = fread(buffer, 1, kBufferSize, pFile);
  402. if(ferror(pFile) == 0) // If succeeded...
  403. {
  404. buffer[kBufferSize - 1] = 0;
  405. // To do.
  406. // buffer has an arbitrary number of 0-terminated strings layed one after another.
  407. // Allocate an array of char pointers or use a static array of arrays. We can simply copy buffer to a permanent buffer and index its strings.
  408. // Need to free an allocated array on shutdown.
  409. // Check to make sure that the strlen wasn't too long.
  410. }
  411. fclose(pFile);
  412. }
  413. }
  414. savedArgv = emptyNonConstString;
  415. return savedArgv;
  416. }
  417. */
  418. #endif
  419. /*
  420. #elif defined(EA_PLATFORM_BSD) || (defined(EA_PLATFORM_SONY) && (defined(EA_PROCESSOR_X86) || defined(EA_PROCESSOR_X86_64)))
  421. Need to make this debug-only for proprietary platforms.
  422. // A way to read the current process path:
  423. // http://linux.die.net/man/2/readlink
  424. #if defined(EA_PLATFORM_SONY)
  425. ssize_t readlink(char *path, char *buf, size_t count)
  426. {
  427. int result;
  428. __asm__ __volatile__(
  429. "mov %%rcx, %%r10\n\t"
  430. "syscall\n\t"
  431. : "=a"(result) : "a"(58), "D"(path), "S"(buf), "d"(count));
  432. return result;
  433. }
  434. #endif
  435. char buf[1024];
  436. char buff[1024];
  437. sprintf(buff, "/dev/%d/file", getpid());
  438. size_t s = readlink(buff, buf, sizeof(buf));
  439. */
  440. #else
  441. EASTDC_API size_t GetCurrentProcessPath(char16_t* pPath, int pathCapacity, int pathFlags)
  442. {
  443. EA_ASSERT(pathCapacity > 0);
  444. char8_t path8[kMaxPathLength];
  445. GetCurrentProcessPath(path8, EAArrayCount(path8), pathFlags);
  446. const int intendedStrlen = Strlcpy(pPath, path8, (size_t)pathCapacity);
  447. if((intendedStrlen >= 0) && (intendedStrlen < pathCapacity)) // If successful (UTF8 to UTF16 conversions can theoretically fail, if the encoded chars are bad)...
  448. return (size_t)intendedStrlen;
  449. pPath[0] = 0;
  450. return 0;
  451. }
  452. EASTDC_API size_t GetCurrentProcessPath(char8_t* pPath, int pathCapacity, int /*pathFlags*/)
  453. {
  454. EA_ASSERT(pathCapacity > 0);
  455. #if EASTDC_SETCURRENTPROCESSPATH_REQUIRED
  456. const size_t intendedStrlen = Strlcpy(pPath, gCurrentProcessPath, pathCapacity);
  457. if(intendedStrlen < (size_t)pathCapacity) // If it completely fit...
  458. return intendedStrlen;
  459. #else
  460. EA_UNUSED(pathCapacity);
  461. #endif
  462. pPath[0] = 0;
  463. return 0;
  464. }
  465. EASTDC_API size_t GetCurrentProcessDirectory(char16_t* pDirectory, int pathCapacity, int pathFlags)
  466. {
  467. char8_t dir8[kMaxDirectoryLength];
  468. GetCurrentProcessDirectory(dir8, EAArrayCount(dir8), pathFlags);
  469. const int intendedStrlen = Strlcpy(pDirectory, dir8, (size_t)pathCapacity);
  470. if((intendedStrlen >= 0) && (intendedStrlen < pathCapacity)) // If successful (UTF8 to UTF16 conversions can theoretically fail, if the encoded chars are bad)...
  471. return (size_t)intendedStrlen;
  472. pDirectory[0] = 0;
  473. return 0;
  474. }
  475. EASTDC_API size_t GetCurrentProcessDirectory(char8_t* pDirectory, int pathCapacity, int pathFlags)
  476. {
  477. EA_ASSERT(pathCapacity > 0);
  478. size_t len = GetCurrentProcessPath(pDirectory, pathCapacity, pathFlags);
  479. if(len > 0)
  480. {
  481. for(int pos = (int)len; pos > 0; --pos)
  482. {
  483. // We make a broad assumption that both / and \ are directory separators. On a number of unsual platforms
  484. // we deal with, / is the norm but \ can still be used. e.g. /host/C:\SomeDir\SomeFile.txt
  485. if((pDirectory[pos - 1] != '/') && (pDirectory[pos - 1] != '\\'))
  486. pDirectory[pos - 1] = 0;
  487. else
  488. break;
  489. }
  490. return Strlen(pDirectory);
  491. }
  492. pDirectory[0] = 0;
  493. return 0;
  494. }
  495. #endif
  496. // The 32 bit versions of GetCurrentProcessPath and GetCurrentProcessDirectory are the same generic
  497. // versions for all platforms, as they just route to using the platform-specific versions.
  498. EASTDC_API size_t GetCurrentProcessPath(char32_t* pPath, int pathCapacity, int pathFlags)
  499. {
  500. EA_ASSERT(pathCapacity > 0); EA_UNUSED(pathCapacity);
  501. char8_t path8[kMaxPathLength];
  502. GetCurrentProcessPath(path8, EAArrayCount(path8), pathFlags);
  503. const int intendedStrlen = Strlcpy(pPath, path8, (size_t)pathCapacity);
  504. if((intendedStrlen >= 0) && (intendedStrlen < pathCapacity)) // If successful...
  505. return (size_t)intendedStrlen;
  506. pPath[0] = 0;
  507. return 0;
  508. }
  509. EASTDC_API size_t GetCurrentProcessDirectory(char32_t* pDirectory, int pathCapacity, int pathFlags)
  510. {
  511. EA_ASSERT(pathCapacity > 0); EA_UNUSED(pathCapacity);
  512. char8_t path8[kMaxDirectoryLength]; // We don't have access to EAIO here.
  513. GetCurrentProcessDirectory(path8, EAArrayCount(path8), pathFlags);
  514. const int intendedStrlen = Strlcpy(pDirectory, path8, (size_t)pathCapacity);
  515. if((intendedStrlen >= 0) && (intendedStrlen < pathCapacity)) // If successful...
  516. return (size_t)intendedStrlen;
  517. pDirectory[0] = 0;
  518. return 0;
  519. }
  520. EASTDC_API size_t GetEnvironmentVar(const char16_t* pName, char16_t* pValue, size_t valueCapacity)
  521. {
  522. #if defined(EA_PLATFORM_WINDOWS) && EA_WINAPI_FAMILY_PARTITION(EA_WINAPI_PARTITION_DESKTOP)
  523. DWORD dwLength = GetEnvironmentVariableW(reinterpret_cast<const wchar_t *>(pName), reinterpret_cast<LPWSTR>(pValue), (DWORD)valueCapacity);
  524. if(dwLength == 0)
  525. {
  526. if(GetLastError() == ERROR_ENVVAR_NOT_FOUND)
  527. return (size_t)-1;
  528. }
  529. else if(dwLength > valueCapacity)
  530. dwLength -= 1; // On insufficient capacity, Windows returns the required capacity.
  531. return (size_t)dwLength;
  532. #else
  533. char8_t name8[260];
  534. char8_t value8[260];
  535. Strlcpy(name8, pName, 260);
  536. const size_t len = GetEnvironmentVar(name8, value8, 260);
  537. if(len < 260)
  538. return (size_t)Strlcpy(pValue, value8, valueCapacity, len);
  539. return len; // Note that the len here is for UTF8 chars, but the user is asking for 16 bit chars. So the returned len may be higher than the actual required len.
  540. #endif
  541. }
  542. EASTDC_API size_t GetEnvironmentVar(const char8_t* pName, char8_t* pValue, size_t valueCapacity)
  543. {
  544. #if defined(EA_PLATFORM_WINDOWS) && EA_WINAPI_FAMILY_PARTITION(EA_WINAPI_PARTITION_DESKTOP)
  545. DWORD dwLength = GetEnvironmentVariableA(pName, pValue, (DWORD)valueCapacity);
  546. if(dwLength == 0)
  547. {
  548. if(GetLastError() == ERROR_ENVVAR_NOT_FOUND)
  549. return (size_t)-1;
  550. }
  551. else if(dwLength > valueCapacity)
  552. dwLength -= 1; // On insufficient capacity, Windows returns the required capacity.
  553. return (size_t)dwLength;
  554. #elif defined(EA_PLATFORM_UNIX)
  555. const char8_t* const var = getenv(pName);
  556. if (var)
  557. return Strlcpy(pValue, var, valueCapacity);
  558. return (size_t)-1;
  559. #else
  560. // To consider: Implement this manually for the given platform.
  561. // Environment variables are application globals and so we probably need to use our OSGlobal to implement this.
  562. EA_UNUSED(pName);
  563. EA_UNUSED(pValue);
  564. EA_UNUSED(valueCapacity);
  565. return (size_t)-1;
  566. #endif
  567. }
  568. EASTDC_API bool SetEnvironmentVar(const char16_t* pName, const char16_t* pValue)
  569. {
  570. #if defined(EA_PLATFORM_WINDOWS) && EA_WINAPI_FAMILY_PARTITION(EA_WINAPI_PARTITION_DESKTOP)
  571. const BOOL bResult = SetEnvironmentVariableW(reinterpret_cast<const wchar_t*>(pName), reinterpret_cast<const wchar_t*>(pValue)); // Windows has the same behavior as us: NULL pValue removes the variable.
  572. return (bResult != 0);
  573. #else
  574. char8_t name8[260];
  575. Strlcpy(name8, pName, 260);
  576. char8_t value8[260];
  577. Strlcpy(value8, pValue, 260);
  578. return SetEnvironmentVar(name8, value8);
  579. #endif
  580. }
  581. EASTDC_API bool SetEnvironmentVar(const char8_t* pName, const char8_t* pValue)
  582. {
  583. #if defined(EA_PLATFORM_WINDOWS) && EA_WINAPI_FAMILY_PARTITION(EA_WINAPI_PARTITION_DESKTOP)
  584. const BOOL bResult = SetEnvironmentVariableA(pName, pValue); // Windows has the same behavior as us: NULL pValue removes the variable.
  585. return (bResult != 0);
  586. #elif defined(EA_PLATFORM_UNIX)
  587. // The opinion of the Linux people is that you just shouldn't ever call setenv during application runtime.
  588. // A better solution for us is to use shared mapped memory (shm_open(), mmap()): http://www.ibm.com/developerworks/aix/library/au-spunix_sharedmemory/index.html
  589. if(pValue)
  590. return setenv(pName, pValue, 1) == 0;
  591. else
  592. return unsetenv(pName) == 0;
  593. #else
  594. // To consider: Implement this manually for the given platform.
  595. // Environment variables are application globals and so we probably need to use our OSGlobal to implement this.
  596. // The easiest way for us to implement this is with an stl/eastl map. But we don't currently have access to those
  597. // from this package. So we are currently stuck using something simpler, like a key=value;key=value;key=value... string.
  598. EA_UNUSED(pName);
  599. EA_UNUSED(pValue);
  600. return false;
  601. #endif
  602. }
  603. EASTDC_API int Spawn(const char16_t* pPath, const char16_t* const* pArgumentArray, bool wait)
  604. {
  605. #if defined(EA_PLATFORM_WINDOWS) && EA_WINAPI_FAMILY_PARTITION(EA_WINAPI_PARTITION_DESKTOP)
  606. return int(_wspawnv(wait ? _P_WAIT : _P_DETACH, reinterpret_cast<const wchar_t *>(pPath), reinterpret_cast<const wchar_t* const *>(pArgumentArray)));
  607. #else
  608. EA_UNUSED(pPath);
  609. EA_UNUSED(pArgumentArray);
  610. EA_UNUSED(wait);
  611. // TODO: convert and call char8_t version
  612. EA_FAIL_MESSAGE("Spawn: Not implemented for this platform.");
  613. return -1;
  614. #endif
  615. }
  616. EASTDC_API int Spawn(const char8_t* pPath, const char8_t* const* pArgumentArray, bool wait)
  617. {
  618. #if defined(EA_PLATFORM_WINDOWS) && EA_WINAPI_FAMILY_PARTITION(EA_WINAPI_PARTITION_DESKTOP)
  619. if(wait)
  620. return int(_spawnv(_P_WAIT, pPath, pArgumentArray));
  621. else
  622. return int(_spawnv(_P_DETACH, pPath, pArgumentArray));
  623. #elif defined(EA_PLATFORM_UNIX) && EASTDC_SYS_WAIT_H_AVAILABLE
  624. pid_t id = fork();
  625. if(id == 0) // child
  626. {
  627. //int result =
  628. execv(pPath, (char* const*)pArgumentArray);
  629. exit(errno);
  630. }
  631. if(wait)
  632. {
  633. int status;
  634. waitpid(id, &status, 0); // waitpid() is safer than wait(), and seems currently be available on all OSs that matter to us.
  635. if(WIFEXITED(status))
  636. return WEXITSTATUS(status); // exit value of child
  637. // the child was killed due to a signal. we could find out
  638. // which signal if we wanted, but we're not really doing unix signals.
  639. return -1;
  640. }
  641. return 0;
  642. #else
  643. EA_UNUSED(pPath);
  644. EA_UNUSED(pArgumentArray);
  645. EA_UNUSED(wait);
  646. EA_FAIL_MESSAGE("Spawn: Not implemented for this platform.");
  647. return -1;
  648. #endif
  649. }
  650. EASTDC_API int ExecuteShellCommand(const char16_t* pCommand)
  651. {
  652. #if defined(EA_PLATFORM_WINDOWS) && EA_WINAPI_FAMILY_PARTITION(EA_WINAPI_PARTITION_DESKTOP)
  653. // Todo: verify that newlines work here and support them if not.
  654. return _wsystem(reinterpret_cast<const wchar_t*>(pCommand)); // We could do this via the shell api as well.
  655. #else
  656. char8_t command8[260];
  657. Strlcpy(command8, pCommand, 260);
  658. return ExecuteShellCommand(command8);
  659. #endif
  660. }
  661. int ExecuteShellCommand(const char8_t* pCommand)
  662. {
  663. #if defined(EA_PLATFORM_WINDOWS) && EA_WINAPI_FAMILY_PARTITION(EA_WINAPI_PARTITION_DESKTOP)
  664. // Todo: verify that newlines work here and support them if not.
  665. return system(pCommand); // We could do this via the shell api as well.
  666. #elif defined(EA_PLATFORM_UNIX)
  667. return system(pCommand);
  668. #else
  669. EA_UNUSED(pCommand);
  670. return false;
  671. #endif
  672. }
  673. #if defined(DISABLED_____EA_PLATFORM_UNIX) // Need to implement this in a way that doesn't use EASTL or an allocator.
  674. EASTDC_API bool SearchEnvPathWithMode(const char8_t* pathListVar, const char8_t* fileName, int mode, eastl::string8* fullPath)
  675. {
  676. if (*fileName == '/' || *fileName == '\\')
  677. {
  678. fullPath->assign(fileName);
  679. return access(fileName, F_OK) == 0;
  680. }
  681. const char* pathList = getenv(pathListVar);
  682. if (pathList)
  683. {
  684. const char* pathStart = pathList;
  685. const char* pathEnd = pathStart;
  686. while (true)
  687. {
  688. while ((*pathEnd != ':') && (*pathEnd != 0))
  689. ++pathEnd;
  690. if (pathEnd > pathStart)
  691. {
  692. fullPath->assign(pathStart, pathEnd - pathStart);
  693. if ((*pathEnd != '/') && (*pathEnd != '\\'))
  694. *fullPath += '/';
  695. *fullPath += fileName;
  696. if (access(fullPath->c_str(), F_OK) == 0)
  697. return true;
  698. }
  699. if (*pathEnd == 0) // end explicitly so we don't access outside pathList.
  700. break;
  701. pathEnd++;
  702. pathStart = pathEnd;
  703. }
  704. }
  705. return false;
  706. }
  707. #endif // EA_PLATFORM_UNIX
  708. EASTDC_API bool SearchEnvironmentPath(const char16_t* pFileName, char16_t* pPath, const char16_t* pEnvironmentVar)
  709. {
  710. #if defined(EA_PLATFORM_WINDOWS) && EA_WINAPI_FAMILY_PARTITION(EA_WINAPI_PARTITION_DESKTOP)
  711. if(!pEnvironmentVar)
  712. pEnvironmentVar = EA_CHAR16("PATH");
  713. _wsearchenv(reinterpret_cast<const wchar_t*>(pFileName), reinterpret_cast<const wchar_t*>(pEnvironmentVar), reinterpret_cast<wchar_t*>(pPath));
  714. return (*pPath != 0);
  715. #else
  716. char8_t path8 [260];
  717. char8_t fileName8[260];
  718. Strlcpy(path8, pPath, 260);
  719. Strlcpy(fileName8, pFileName, 260);
  720. bool success;
  721. if (pEnvironmentVar)
  722. {
  723. char8_t environmentVariable8[260];
  724. Strlcpy(environmentVariable8, pEnvironmentVar, 260);
  725. success = EA::StdC::SearchEnvironmentPath(fileName8, path8, environmentVariable8);
  726. }
  727. else
  728. success = EA::StdC::SearchEnvironmentPath(fileName8, path8);
  729. Strlcpy(pPath, path8, 260);
  730. return success;
  731. #endif
  732. }
  733. EASTDC_API bool SearchEnvironmentPath(const char8_t* pFileName, char8_t* pPath, const char8_t* pEnvironmentVar)
  734. {
  735. #if defined(EA_PLATFORM_WINDOWS) && EA_WINAPI_FAMILY_PARTITION(EA_WINAPI_PARTITION_DESKTOP)
  736. if(!pEnvironmentVar)
  737. pEnvironmentVar = "PATH";
  738. _searchenv(pFileName, pEnvironmentVar, pPath);
  739. return (*pPath != 0);
  740. #elif defined(DISABLED_____EA_PLATFORM_UNIX) // Need to implement this in a way that doesn't use EASTL or an allocator.
  741. eastl::string8 path8(EASTLAllocatorType(UTFFOUNDATION_ALLOC_PREFIX "EAProcess"));
  742. bool success;
  743. if (pEnvironmentVar)
  744. success = SearchEnvPathWithMode(pEnvironmentVar, pFileName, F_OK, &path8); // Just require existence.
  745. else
  746. success = SearchEnvPathWithMode("PATH", pFileName, X_OK, &path8); // Require executability.
  747. if (success)
  748. {
  749. Strcpy(pPath, path8.c_str());
  750. return true;
  751. }
  752. return false;
  753. #else
  754. EA_UNUSED(pFileName);
  755. EA_UNUSED(pPath);
  756. EA_UNUSED(pEnvironmentVar);
  757. return false;
  758. #endif
  759. }
  760. #if defined(EA_PLATFORM_WINDOWS) && EA_WINAPI_FAMILY_PARTITION(EA_WINAPI_PARTITION_DESKTOP)
  761. namespace // anonymous namespace for this file.
  762. {
  763. typedef HINSTANCE (APIENTRY* ShellExecuteWFunctionType)(HWND hwnd, LPCWSTR lpOperation, LPCWSTR lpFile, LPCWSTR lpParameters, LPCWSTR lpDirectory, INT nShowCmd);
  764. static ShellExecuteWFunctionType ShellExecuteWFunction = NULL;
  765. static HINSTANCE hShellExecuteWFunctionLibrary = NULL;
  766. struct ShellExecuteWFunctionEntryPointFinder
  767. {
  768. ShellExecuteWFunctionEntryPointFinder()
  769. {
  770. hShellExecuteWFunctionLibrary = LoadLibraryW(EA_WCHAR("shell32.dll"));
  771. if(hShellExecuteWFunctionLibrary)
  772. ShellExecuteWFunction = (ShellExecuteWFunctionType)(void*)::GetProcAddress(hShellExecuteWFunctionLibrary, "ShellExecuteW");
  773. }
  774. ~ShellExecuteWFunctionEntryPointFinder()
  775. {
  776. if(hShellExecuteWFunctionLibrary)
  777. ::FreeLibrary(hShellExecuteWFunctionLibrary);
  778. }
  779. };
  780. }
  781. EASTDC_API bool OpenFile(const char16_t* pPath)
  782. {
  783. HINSTANCE hInstance = 0;
  784. ShellExecuteWFunctionEntryPointFinder sShellExecuteWFunctionEntryPointFinder;
  785. if(ShellExecuteWFunction)
  786. {
  787. if(Strstr(pPath, EA_CHAR16("http://")) == pPath) // If the path begins with "http://" and is thus a URL...
  788. {
  789. wchar_t pathMod[260 + 4];
  790. Strcpy(pathMod, EA_WCHAR("url:"));
  791. Strlcat(pathMod, reinterpret_cast<const wchar_t*>(pPath), 260 + 4); // ShellExecute wants the path to look like this: "url:http://www.bozo.com"
  792. hInstance = ShellExecuteWFunction(NULL, EA_WCHAR("open"), pathMod, NULL, NULL, SW_SHOWNORMAL);
  793. }
  794. else
  795. {
  796. hInstance = ShellExecuteWFunction(NULL, EA_WCHAR("open"), reinterpret_cast<const wchar_t*>(pPath), NULL, NULL, SW_SHOWNORMAL);
  797. }
  798. }
  799. return ((uintptr_t)hInstance > 32);
  800. }
  801. EASTDC_API bool OpenFile(const char8_t* pPath)
  802. {
  803. char16_t path16[260];
  804. Strlcpy(path16, pPath, 260);
  805. return OpenFile(path16);
  806. }
  807. #else
  808. EASTDC_API bool OpenFile(const char16_t* pPath)
  809. {
  810. char8_t path8[260];
  811. Strlcpy(path8, pPath, 260);
  812. return OpenFile(path8);
  813. }
  814. EASTDC_API bool OpenFile(const char8_t* pPath)
  815. {
  816. #if defined (EA_PLATFORM_OSX)
  817. const char8_t* args[] =
  818. {
  819. "open",
  820. pPath,
  821. 0
  822. };
  823. return Spawn("/usr/bin/open", args) != -1;
  824. #else
  825. EA_UNUSED(pPath);
  826. return false;
  827. #endif
  828. }
  829. #endif // EA_PLATFORM_WINDOWS
  830. } // namespace StdC
  831. } // namespace EA