| 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075 |
- ///////////////////////////////////////////////////////////////////////////////
- // Copyright (c) Electronic Arts Inc. All rights reserved.
- ///////////////////////////////////////////////////////////////////////////////
- ///////////////////////////////////////////////////////////////////////////////
- // This module defines functions for process spawning and query.
- /////////////////////////////////////////////////////////////////////////////
- #include <EAStdC/internal/Config.h>
- #include <EAStdC/EAProcess.h>
- #include <EAStdC/EAString.h>
- #include <string.h>
- #include <EAAssert/eaassert.h>
- #if defined(EA_PLATFORM_MICROSOFT)
- #pragma warning(push, 0)
- #include <Windows.h>
- #include <stdlib.h>
- #include <process.h>
- #if EA_WINAPI_FAMILY_PARTITION(EA_WINAPI_PARTITION_DESKTOP)
- #include <ShellAPI.h>
- #pragma warning(pop)
- #ifdef _MSC_VER
- #pragma comment(lib, "shell32.lib") // Required for shellapi calls.
- #endif
- #endif
- #elif defined(EA_PLATFORM_UNIX)
- #include <sys/types.h>
- #if EASTDC_SYS_WAIT_H_AVAILABLE
- #include <sys/wait.h>
- #endif
- #include <unistd.h>
- #include <errno.h>
- #include <stdio.h>
- #elif defined(EA_PLATFORM_SONY) && EA_SCEDBG_ENABLED
- #include <libdbg.h>
- #endif
- #if defined(EA_PLATFORM_APPLE)
- #include <mach-o/dyld.h> // _NSGetExecutablePath
- #include <sys/syslimits.h> // PATH_MAX
- #include <libgen.h> // dirname
- namespace EA
- {
- namespace StdC
- {
- //Used to determine if a given path is a bundle extension
- const char8_t* kBundleExtensions[] = {
- ".app",
- ".bundle",
- ".plugin"
- };
- }
- }
- #endif
- namespace EA
- {
- namespace StdC
- {
- // EASTDC_SETCURRENTPROCESSPATH_REQUIRED
- //
- // Defined as 0 or 1.
- //
- #ifndef EASTDC_SETCURRENTPROCESSPATH_REQUIRED
- #if defined(EA_PLATFORM_SONY) && defined(EA_PLATFORM_CONSOLE)
- #define EASTDC_SETCURRENTPROCESSPATH_REQUIRED 1
- #else
- #define EASTDC_SETCURRENTPROCESSPATH_REQUIRED 0
- #endif
- #endif
- #if EASTDC_SETCURRENTPROCESSPATH_REQUIRED
- static char8_t gCurrentProcessPath[kMaxPathLength] = { 0 };
- #endif
- EASTDC_API void SetCurrentProcessPath(const char8_t* pPath)
- {
- #if EASTDC_SETCURRENTPROCESSPATH_REQUIRED
- Strlcpy(gCurrentProcessPath, pPath, EAArrayCount(gCurrentProcessPath));
- #else
- EA_UNUSED(pPath);
- #endif
- }
- #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)
- // According to Microsoft documentation:
- // The GetModuleFileName function retrieves the full path and file name
- // for the file containing the specified module.
- // If the function succeeds, the return value is the length of the string
- // copied to the buffer, in TCHARs. If the buffer is too small to hold the
- // module name, the string is truncated to the user-supplied capacity, and
- // the function returns that capacity.
- EASTDC_API size_t GetCurrentProcessPath(char16_t* pPath, int pathCapacity, int /*pathFlags*/)
- {
- EA_ASSERT(pathCapacity > 0); EA_UNUSED(pathCapacity);
- const DWORD dwResult = GetModuleFileNameW(NULL, reinterpret_cast<LPWSTR>(pPath), (DWORD)pathCapacity);
- if((dwResult != 0) && (dwResult < (DWORD)pathCapacity)) // If there wasn't an error and there was enough capacity...
- return (size_t)dwResult;
- pPath[0] = 0;
- return 0;
- }
- EASTDC_API size_t GetCurrentProcessPath(char8_t* pPath, int pathCapacity, int pathFlags)
- {
- EA_ASSERT(pathCapacity > 0); EA_UNUSED(pathCapacity);
- // We cannot use GetModuleFileNameA here, because the text encoding of
- // GetModuleFileNameA is arbitrary and in any case is usually not UTF8.
- char16_t path16[kMaxPathLength];
- GetCurrentProcessPath(path16, EAArrayCount(path16), pathFlags);
- const int intendedStrlen = Strlcpy(pPath, path16, (size_t)pathCapacity);
- if((intendedStrlen >= 0) && (intendedStrlen < pathCapacity))
- return (size_t)intendedStrlen;
- pPath[0] = 0;
- return 0;
- }
- EASTDC_API size_t GetCurrentProcessDirectory(char16_t* pDirectory, int pathCapacity, int /*pathFlags*/)
- {
- EA_ASSERT(pathCapacity > 0); EA_UNUSED(pathCapacity);
- const DWORD dwResult = GetModuleFileNameW(NULL, reinterpret_cast<LPWSTR>(pDirectory), (DWORD)pathCapacity);
- if((dwResult != 0) && (dwResult < (DWORD)pathCapacity)) // If there wasn't an error and there was enough capacity...
- {
- DWORD dw;
- for(dw = dwResult; dw > 0; --dw)
- {
- if((pDirectory[dw - 1] != '/') && (pDirectory[dw - 1] != '\\'))
- pDirectory[dw - 1] = 0;
- else
- break;
- }
- return dw;
- }
- pDirectory[0] = 0;
- return 0;
- }
- EASTDC_API size_t GetCurrentProcessDirectory(char8_t* pDirectory, int pathCapacity, int pathFlags)
- {
- EA_ASSERT(pathCapacity > 0); EA_UNUSED(pathCapacity);
- char16_t path16[kMaxDirectoryLength];
- GetCurrentProcessDirectory(path16, EAArrayCount(path16), pathFlags);
- const int intendedStrlen = Strlcpy(pDirectory, path16, (size_t)pathCapacity);
- if((intendedStrlen >= 0) && (intendedStrlen < pathCapacity))
- return (size_t)intendedStrlen;
- pDirectory[0] = 0;
- return 0;
- }
- #elif defined(EA_PLATFORM_SONY) && EA_SCEDBG_ENABLED // Debug time only, as the following code would not be allowed for use in retail kits.
- // sceDbgGetExecutablePath
- // Gets the application file path. The path can only be obtained for applications run
- // from the host PC (run by Visual Studio, Neighborhood or the -run command).
- EASTDC_API size_t GetCurrentProcessPath(char8_t* pPath, int pathCapacity, int /*pathFlags*/)
- {
- EA_ASSERT(pathCapacity > 0);
- size_t result;
- // If the user has set the process path, use the setting instead of querying.
- if (gCurrentProcessPath[0])
- {
- result = Strlcpy(pPath, gCurrentProcessPath, pathCapacity);
- }
- else
- {
- result = (size_t)sceDbgGetExecutablePath(pPath, (size_t)(unsigned)pathCapacity);
- }
- if(result < (size_t)pathCapacity) // sceDbgGetExecutablePath returns the requires strlen.
- {
- return result;
- }
- else
- {
- pPath[0] = 0;
- return 0; // sceDbgGetExecutablePath always 0-terminates, even on too small capacity.
- }
- }
- EASTDC_API size_t GetCurrentProcessPath(char16_t* pPath, int pathCapacity, int pathFlags)
- {
- EA_ASSERT(pathCapacity > 0);
- char8_t path8[kMaxPathLength];
- GetCurrentProcessPath(path8, EAArrayCount(path8), pathFlags);
- const int intendedStrlen = Strlcpy(pPath, path8, pathCapacity);
- if((intendedStrlen >= 0) && (intendedStrlen < pathCapacity)) // If successful (UTF8 to UTF16 conversions can theoretically fail, if the encoded chars are bad...
- return (size_t)intendedStrlen;
- pPath[0] = 0;
- return 0;
- }
- EASTDC_API size_t GetCurrentProcessDirectory(char8_t* pDirectory, int pathCapacity, int /*pathFlags*/)
- {
- int32_t result;
- if (gCurrentProcessPath[0])
- {
- result = (int32_t)Strlcpy(pDirectory, gCurrentProcessPath, pathCapacity);
- }
- else
- {
- result = sceDbgGetExecutablePath(pDirectory, (size_t)(unsigned)pathCapacity);
- }
- if(result < pathCapacity) // sceDbgGetExecutablePath returns the requires strlen.
- {
- while (result > 0)
- {
- if (pDirectory[result - 1] == '/' || pDirectory[result - 1] == '\\')
- break;
-
- pDirectory[--result] = '\0';
- }
- return (size_t)(uint32_t)(result);
- }
- else
- {
- pDirectory[0] = 0;
- return 0; // sceDbgGetExecutablePath always 0-terminates, even on too small capacity.
- }
- }
- EASTDC_API size_t GetCurrentProcessDirectory(char16_t* pDirectory, int pathCapacity, int pathFlags)
- {
- EA_ASSERT(pathCapacity > 0);
- char8_t path8[kMaxDirectoryLength]; // We don't have access to EAIO here.
- GetCurrentProcessDirectory(path8, EAArrayCount(path8), pathFlags);
- const int intendedStrlen = Strlcpy(pDirectory, path8, pathCapacity);
- if((intendedStrlen >= 0) && (intendedStrlen < pathCapacity)) // If successful (UTF8 to UTF16 conversions can theoretically fail, if the encoded chars are bad...
- return (size_t)intendedStrlen;
- pDirectory[0] = 0;
- return 0;
- }
- #elif defined(EA_PLATFORM_APPLE)
- EASTDC_API size_t GetCurrentProcessPath(char16_t* pPath, int pathCapacity, int pathFlags)
- {
- EA_ASSERT(pathCapacity > 0); EA_UNUSED(pathCapacity);
- char8_t path8[kMaxPathLength];
- GetCurrentProcessPath(path8, EAArrayCount(path8), pathFlags);
- const int intendedStrlen = Strlcpy(pPath, path8, pathCapacity);
- if((intendedStrlen >= 0) && (intendedStrlen < pathCapacity)) // If successful (UTF8 to UTF16 conversions can theoretically fail, if the encoded chars are bad...
- return (size_t)intendedStrlen;
- pPath[0] = 0;
- return 0;
- }
-
- static bool IsBundleFolder(char8_t* pPath, int pathCapacity)
- {
- for(size_t i = 0; i < EAArrayCount(kBundleExtensions); i++)
- {
- if(Striend(pPath, kBundleExtensions[i]))
- {
- return true;
- }
- }
- return false;
- }
-
- // 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
- // EG: /path/to/MyApp.extension or /path/to/MyApp.extension/MyExecutable
- // Currently /path/to/.extension is returned if it exists, otherwise it returns the executable path
- EASTDC_API size_t GetCurrentProcessPath(char8_t* pPath, int pathCapacity, int pathFlags)
- {
- EA_ASSERT(pathCapacity > 0);
- // https://developer.apple.com/library/mac/#documentation/Darwin/Reference/ManPages/man3/dyld.3.html
- // http://lists.apple.com/archives/darwin-dev/2008/Dec/msg00037.html
-
- uint32_t capacityU32 = (uint32_t)pathCapacity;
- int result = _NSGetExecutablePath(pPath, &capacityU32); // Returns -1 and sets capacityU32 if the capacity is not enough.
-
- if(result == 0)
- {
- EA_ASSERT(pathCapacity >= kMaxPathLength);
- char8_t absolutePath[PATH_MAX];
- if(realpath(pPath, absolutePath) != NULL) // Obtain canonicalized absolute pathname.
- {
- if(pathFlags & kPathFlagBundlePath)
- {
- // We recursively call dirname() until we find .extension
- char8_t appPath[kMaxPathLength];
- EA::StdC::Strlcpy(appPath, absolutePath, kMaxPathLength);
- bool found = IsBundleFolder(appPath, kMaxPathLength);
- while(!found &&
- EA::StdC::Strncmp(appPath, ".", kMaxPathLength) != 0 &&
- EA::StdC::Strncmp(appPath, "/", kMaxPathLength) != 0)
- {
- EA::StdC::Strlcpy(appPath,dirname(appPath), kMaxPathLength);
- found = IsBundleFolder(appPath, kMaxPathLength);
- }
-
- if(found)
- EA::StdC::Strlcpy(pPath, appPath, pathCapacity);
- else // If not found, we use the original executable absolute path.
- EA::StdC::Strlcpy(pPath, absolutePath, pathCapacity);
- }
- else
- EA::StdC::Strlcpy(pPath, absolutePath, pathCapacity);
- return Strlen(pPath);
- }
- }
-
- pPath[0] = 0;
- return 0;
- }
- EASTDC_API size_t GetCurrentProcessDirectory(char16_t* pDirectory, int pathCapacity, int pathFlags)
- {
- EA_ASSERT(pathCapacity > 0); EA_UNUSED(pathCapacity);
- char8_t path8[kMaxDirectoryLength]; // We don't have access to EAIO here.
- GetCurrentProcessDirectory(path8, EAArrayCount(path8), pathFlags);
- const int intendedStrlen = Strlcpy(pDirectory, path8, pathCapacity);
- if((intendedStrlen >= 0) && (intendedStrlen < pathCapacity)) // If successful (UTF8 to UTF16 conversions can theoretically fail, if the encoded chars are bad...
- return (size_t)intendedStrlen;
- pDirectory[0] = 0;
- return 0;
- }
- EASTDC_API size_t GetCurrentProcessDirectory(char8_t* pDirectory, int pathCapacity, int pathFlags)
- {
- EA_ASSERT(pathCapacity > 0);
- char pAppPath[kMaxPathLength];
- size_t n = GetCurrentProcessPath(pAppPath, kMaxPathLength, pathFlags);
-
- if(n > 0)
- {
- // argv[0] pDirectory
- // --------------------------------------------------
- // "" -> "" (Should never happen)
- // "/" -> "/" (Should never happen)
- // "a" -> "a" (Should never happen)
- // "/a/b" -> /a/"
- EA_COMPILETIME_ASSERT(kMaxDirectoryLength >= kMaxPathLength); // We assert this because argv[0] could concievably be as long as kMaxPathLength.
- const size_t intendedStrlen = Strlcpy(pDirectory, pAppPath, pathCapacity);
- if(intendedStrlen < (size_t)pathCapacity) // If succeeded...
- {
- for(char8_t* p = pDirectory + intendedStrlen; p > pDirectory; --p)
- {
- if(p[-1] == '/')
- {
- p[0] = 0; // e.g. /aaa/bbb/ccc => /aaa/bbb/
- return (size_t)(p - pDirectory);
- }
- }
- // Alternative implementation which we should validate, as it's simpler:
- //char* p = strrchr(pDirectory, '/');
- //if(p) // This should usually (always?) be valid.
- // *p = 0; // e.g. /aaa/bbb/ccc => /aaa/bbb/
- return Strlen(pDirectory);
- }
- }
-
- pDirectory[0] = 0;
- return 0;
- }
- #elif defined(EA_PLATFORM_LINUX)
- EASTDC_API size_t GetCurrentProcessPath(char8_t* pPath, int pathCapacity, int /*pathFlags*/)
- {
- EA_ASSERT(pathCapacity > 0); EA_UNUSED(pathCapacity);
- ssize_t resultLen = readlink("/proc/self/exe", pPath, pathCapacity);
- if( resultLen != -1 )
- {
- ssize_t terminatorLocation = resultLen < (pathCapacity-1) ? resultLen : (pathCapacity-1);
- pPath[terminatorLocation] = '\0';
- return terminatorLocation;
- }
- else
- {
- pPath[0] = 0;
- return 0;
- }
- }
- EASTDC_API size_t GetCurrentProcessPath(char16_t* pPath, int pathCapacity, int pathFlags)
- {
- EA_ASSERT(pathCapacity > 0); EA_UNUSED(pathCapacity);
- char8_t path8[kMaxPathLength];
- GetCurrentProcessPath(path8, EAArrayCount(path8), pathFlags);
- const int intendedStrlen = Strlcpy(pPath, path8, pathCapacity);
- if((intendedStrlen >= 0) && (intendedStrlen < pathCapacity)) // If successful (UTF8 to UTF16 conversions can theoretically fail, if the encoded chars are bad...
- return (size_t)intendedStrlen;
- pPath[0] = 0;
- return 0;
- }
- EASTDC_API size_t GetCurrentProcessDirectory(char8_t* pDirectory, int pathCapacity, int /*pathFlags*/)
- {
- EA_ASSERT(pathCapacity > 0); EA_UNUSED(pathCapacity);
- ssize_t resultLen = readlink("/proc/self/exe", pDirectory, pathCapacity);
- if( resultLen != -1 )
- {
- for(int pos = resultLen; pos > 0; --pos)
- {
- if(pDirectory[pos - 1] != '/')
- pDirectory[pos - 1] = 0;
- else
- break;
- }
- return strlen(pDirectory);
- }
- else
- {
- pDirectory[0] = 0;
- return 0;
- }
- }
- EASTDC_API size_t GetCurrentProcessDirectory(char16_t* pDirectory, int pathCapacity, int pathFlags)
- {
- EA_ASSERT(pathCapacity > 0); EA_UNUSED(pathCapacity);
- char8_t path8[kMaxDirectoryLength]; // We don't have access to EAIO here.
- GetCurrentProcessDirectory(path8, EAArrayCount(path8), pathFlags);
- const int intendedStrlen = Strlcpy(pDirectory, path8, pathCapacity);
- if((intendedStrlen >= 0) && (intendedStrlen < pathCapacity)) // If successful (UTF8 to UTF16 conversions can theoretically fail, if the encoded chars are bad...
- return (size_t)intendedStrlen;
- pDirectory[0] = 0;
- return 0;
- }
- #if 0
- /*
- // http://blog.linuxgamepublishing.com/2009/10/12/argv-and-argc-and-just-how-to-get-them/
- // http://stackoverflow.com/questions/1585989/how-to-parse-proc-pid-cmdline
- // https://www.google.com/search?q=%2Fproc%2Fself%2Fcmdline
- char** get_argv()
- {
- static char emptyNonConstString[1][1] = { { 0 } };
- static char** savedArgv = NULL;
-
- if(!savedArgv)
- {
- FILE* pFile = fopen("/proc/self/cmdline", "r");
-
- if(pFile) // This should be true for at least all recent Linux versions.
- {
- const size_t kBufferSize = 1024; // This should be dynamically allocated if we are to be able to ready any buffer.
- char buffer[kBufferSize];
- size_t count = fread(buffer, 1, kBufferSize, pFile);
-
- if(ferror(pFile) == 0) // If succeeded...
- {
- buffer[kBufferSize - 1] = 0;
-
- // To do.
- // buffer has an arbitrary number of 0-terminated strings layed one after another.
- // 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.
- // Need to free an allocated array on shutdown.
- // Check to make sure that the strlen wasn't too long.
- }
- fclose(pFile);
- }
- }
-
- savedArgv = emptyNonConstString;
- return savedArgv;
- }
- */
- #endif
- /*
- #elif defined(EA_PLATFORM_BSD) || (defined(EA_PLATFORM_SONY) && (defined(EA_PROCESSOR_X86) || defined(EA_PROCESSOR_X86_64)))
- Need to make this debug-only for proprietary platforms.
- // A way to read the current process path:
- // http://linux.die.net/man/2/readlink
- #if defined(EA_PLATFORM_SONY)
- ssize_t readlink(char *path, char *buf, size_t count)
- {
- int result;
- __asm__ __volatile__(
- "mov %%rcx, %%r10\n\t"
- "syscall\n\t"
- : "=a"(result) : "a"(58), "D"(path), "S"(buf), "d"(count));
- return result;
- }
- #endif
- char buf[1024];
- char buff[1024];
- sprintf(buff, "/dev/%d/file", getpid());
- size_t s = readlink(buff, buf, sizeof(buf));
- */
- #else
- EASTDC_API size_t GetCurrentProcessPath(char16_t* pPath, int pathCapacity, int pathFlags)
- {
- EA_ASSERT(pathCapacity > 0);
- char8_t path8[kMaxPathLength];
- GetCurrentProcessPath(path8, EAArrayCount(path8), pathFlags);
- const int intendedStrlen = Strlcpy(pPath, path8, (size_t)pathCapacity);
- if((intendedStrlen >= 0) && (intendedStrlen < pathCapacity)) // If successful (UTF8 to UTF16 conversions can theoretically fail, if the encoded chars are bad)...
- return (size_t)intendedStrlen;
- pPath[0] = 0;
- return 0;
- }
- EASTDC_API size_t GetCurrentProcessPath(char8_t* pPath, int pathCapacity, int /*pathFlags*/)
- {
- EA_ASSERT(pathCapacity > 0);
- #if EASTDC_SETCURRENTPROCESSPATH_REQUIRED
- const size_t intendedStrlen = Strlcpy(pPath, gCurrentProcessPath, pathCapacity);
- if(intendedStrlen < (size_t)pathCapacity) // If it completely fit...
- return intendedStrlen;
- #else
- EA_UNUSED(pathCapacity);
- #endif
- pPath[0] = 0;
- return 0;
- }
- EASTDC_API size_t GetCurrentProcessDirectory(char16_t* pDirectory, int pathCapacity, int pathFlags)
- {
- char8_t dir8[kMaxDirectoryLength];
- GetCurrentProcessDirectory(dir8, EAArrayCount(dir8), pathFlags);
- const int intendedStrlen = Strlcpy(pDirectory, dir8, (size_t)pathCapacity);
- if((intendedStrlen >= 0) && (intendedStrlen < pathCapacity)) // If successful (UTF8 to UTF16 conversions can theoretically fail, if the encoded chars are bad)...
- return (size_t)intendedStrlen;
- pDirectory[0] = 0;
- return 0;
- }
- EASTDC_API size_t GetCurrentProcessDirectory(char8_t* pDirectory, int pathCapacity, int pathFlags)
- {
- EA_ASSERT(pathCapacity > 0);
- size_t len = GetCurrentProcessPath(pDirectory, pathCapacity, pathFlags);
- if(len > 0)
- {
- for(int pos = (int)len; pos > 0; --pos)
- {
- // We make a broad assumption that both / and \ are directory separators. On a number of unsual platforms
- // we deal with, / is the norm but \ can still be used. e.g. /host/C:\SomeDir\SomeFile.txt
- if((pDirectory[pos - 1] != '/') && (pDirectory[pos - 1] != '\\'))
- pDirectory[pos - 1] = 0;
- else
- break;
- }
- return Strlen(pDirectory);
- }
- pDirectory[0] = 0;
- return 0;
- }
- #endif
- // The 32 bit versions of GetCurrentProcessPath and GetCurrentProcessDirectory are the same generic
- // versions for all platforms, as they just route to using the platform-specific versions.
- EASTDC_API size_t GetCurrentProcessPath(char32_t* pPath, int pathCapacity, int pathFlags)
- {
- EA_ASSERT(pathCapacity > 0); EA_UNUSED(pathCapacity);
- char8_t path8[kMaxPathLength];
- GetCurrentProcessPath(path8, EAArrayCount(path8), pathFlags);
- const int intendedStrlen = Strlcpy(pPath, path8, (size_t)pathCapacity);
- if((intendedStrlen >= 0) && (intendedStrlen < pathCapacity)) // If successful...
- return (size_t)intendedStrlen;
- pPath[0] = 0;
- return 0;
- }
- EASTDC_API size_t GetCurrentProcessDirectory(char32_t* pDirectory, int pathCapacity, int pathFlags)
- {
- EA_ASSERT(pathCapacity > 0); EA_UNUSED(pathCapacity);
- char8_t path8[kMaxDirectoryLength]; // We don't have access to EAIO here.
- GetCurrentProcessDirectory(path8, EAArrayCount(path8), pathFlags);
- const int intendedStrlen = Strlcpy(pDirectory, path8, (size_t)pathCapacity);
- if((intendedStrlen >= 0) && (intendedStrlen < pathCapacity)) // If successful...
- return (size_t)intendedStrlen;
- pDirectory[0] = 0;
- return 0;
- }
- EASTDC_API size_t GetEnvironmentVar(const char16_t* pName, char16_t* pValue, size_t valueCapacity)
- {
- #if defined(EA_PLATFORM_WINDOWS) && EA_WINAPI_FAMILY_PARTITION(EA_WINAPI_PARTITION_DESKTOP)
- DWORD dwLength = GetEnvironmentVariableW(reinterpret_cast<const wchar_t *>(pName), reinterpret_cast<LPWSTR>(pValue), (DWORD)valueCapacity);
- if(dwLength == 0)
- {
- if(GetLastError() == ERROR_ENVVAR_NOT_FOUND)
- return (size_t)-1;
- }
- else if(dwLength > valueCapacity)
- dwLength -= 1; // On insufficient capacity, Windows returns the required capacity.
- return (size_t)dwLength;
- #else
- char8_t name8[260];
- char8_t value8[260];
- Strlcpy(name8, pName, 260);
- const size_t len = GetEnvironmentVar(name8, value8, 260);
- if(len < 260)
- return (size_t)Strlcpy(pValue, value8, valueCapacity, len);
- 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.
- #endif
- }
- EASTDC_API size_t GetEnvironmentVar(const char8_t* pName, char8_t* pValue, size_t valueCapacity)
- {
- #if defined(EA_PLATFORM_WINDOWS) && EA_WINAPI_FAMILY_PARTITION(EA_WINAPI_PARTITION_DESKTOP)
- DWORD dwLength = GetEnvironmentVariableA(pName, pValue, (DWORD)valueCapacity);
- if(dwLength == 0)
- {
- if(GetLastError() == ERROR_ENVVAR_NOT_FOUND)
- return (size_t)-1;
- }
- else if(dwLength > valueCapacity)
- dwLength -= 1; // On insufficient capacity, Windows returns the required capacity.
- return (size_t)dwLength;
- #elif defined(EA_PLATFORM_UNIX)
- const char8_t* const var = getenv(pName);
- if (var)
- return Strlcpy(pValue, var, valueCapacity);
- return (size_t)-1;
- #else
- // To consider: Implement this manually for the given platform.
- // Environment variables are application globals and so we probably need to use our OSGlobal to implement this.
- EA_UNUSED(pName);
- EA_UNUSED(pValue);
- EA_UNUSED(valueCapacity);
- return (size_t)-1;
- #endif
- }
- EASTDC_API bool SetEnvironmentVar(const char16_t* pName, const char16_t* pValue)
- {
- #if defined(EA_PLATFORM_WINDOWS) && EA_WINAPI_FAMILY_PARTITION(EA_WINAPI_PARTITION_DESKTOP)
- 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.
- return (bResult != 0);
- #else
- char8_t name8[260];
- Strlcpy(name8, pName, 260);
- char8_t value8[260];
- Strlcpy(value8, pValue, 260);
- return SetEnvironmentVar(name8, value8);
- #endif
- }
- EASTDC_API bool SetEnvironmentVar(const char8_t* pName, const char8_t* pValue)
- {
- #if defined(EA_PLATFORM_WINDOWS) && EA_WINAPI_FAMILY_PARTITION(EA_WINAPI_PARTITION_DESKTOP)
- const BOOL bResult = SetEnvironmentVariableA(pName, pValue); // Windows has the same behavior as us: NULL pValue removes the variable.
- return (bResult != 0);
- #elif defined(EA_PLATFORM_UNIX)
- // The opinion of the Linux people is that you just shouldn't ever call setenv during application runtime.
- // 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
- if(pValue)
- return setenv(pName, pValue, 1) == 0;
- else
- return unsetenv(pName) == 0;
- #else
- // To consider: Implement this manually for the given platform.
- // Environment variables are application globals and so we probably need to use our OSGlobal to implement this.
- // The easiest way for us to implement this is with an stl/eastl map. But we don't currently have access to those
- // from this package. So we are currently stuck using something simpler, like a key=value;key=value;key=value... string.
- EA_UNUSED(pName);
- EA_UNUSED(pValue);
- return false;
- #endif
- }
- EASTDC_API int Spawn(const char16_t* pPath, const char16_t* const* pArgumentArray, bool wait)
- {
- #if defined(EA_PLATFORM_WINDOWS) && EA_WINAPI_FAMILY_PARTITION(EA_WINAPI_PARTITION_DESKTOP)
- return int(_wspawnv(wait ? _P_WAIT : _P_DETACH, reinterpret_cast<const wchar_t *>(pPath), reinterpret_cast<const wchar_t* const *>(pArgumentArray)));
- #else
- EA_UNUSED(pPath);
- EA_UNUSED(pArgumentArray);
- EA_UNUSED(wait);
- // TODO: convert and call char8_t version
- EA_FAIL_MESSAGE("Spawn: Not implemented for this platform.");
- return -1;
- #endif
- }
- EASTDC_API int Spawn(const char8_t* pPath, const char8_t* const* pArgumentArray, bool wait)
- {
- #if defined(EA_PLATFORM_WINDOWS) && EA_WINAPI_FAMILY_PARTITION(EA_WINAPI_PARTITION_DESKTOP)
- if(wait)
- return int(_spawnv(_P_WAIT, pPath, pArgumentArray));
- else
- return int(_spawnv(_P_DETACH, pPath, pArgumentArray));
- #elif defined(EA_PLATFORM_UNIX) && EASTDC_SYS_WAIT_H_AVAILABLE
- pid_t id = fork();
- if(id == 0) // child
- {
- //int result =
- execv(pPath, (char* const*)pArgumentArray);
- exit(errno);
- }
- if(wait)
- {
- int status;
- waitpid(id, &status, 0); // waitpid() is safer than wait(), and seems currently be available on all OSs that matter to us.
- if(WIFEXITED(status))
- return WEXITSTATUS(status); // exit value of child
- // the child was killed due to a signal. we could find out
- // which signal if we wanted, but we're not really doing unix signals.
- return -1;
- }
- return 0;
- #else
- EA_UNUSED(pPath);
- EA_UNUSED(pArgumentArray);
- EA_UNUSED(wait);
- EA_FAIL_MESSAGE("Spawn: Not implemented for this platform.");
- return -1;
- #endif
- }
- EASTDC_API int ExecuteShellCommand(const char16_t* pCommand)
- {
- #if defined(EA_PLATFORM_WINDOWS) && EA_WINAPI_FAMILY_PARTITION(EA_WINAPI_PARTITION_DESKTOP)
- // Todo: verify that newlines work here and support them if not.
- return _wsystem(reinterpret_cast<const wchar_t*>(pCommand)); // We could do this via the shell api as well.
- #else
- char8_t command8[260];
- Strlcpy(command8, pCommand, 260);
- return ExecuteShellCommand(command8);
- #endif
- }
- int ExecuteShellCommand(const char8_t* pCommand)
- {
- #if defined(EA_PLATFORM_WINDOWS) && EA_WINAPI_FAMILY_PARTITION(EA_WINAPI_PARTITION_DESKTOP)
- // Todo: verify that newlines work here and support them if not.
- return system(pCommand); // We could do this via the shell api as well.
- #elif defined(EA_PLATFORM_UNIX)
- return system(pCommand);
- #else
- EA_UNUSED(pCommand);
- return false;
- #endif
- }
- #if defined(DISABLED_____EA_PLATFORM_UNIX) // Need to implement this in a way that doesn't use EASTL or an allocator.
- EASTDC_API bool SearchEnvPathWithMode(const char8_t* pathListVar, const char8_t* fileName, int mode, eastl::string8* fullPath)
- {
- if (*fileName == '/' || *fileName == '\\')
- {
- fullPath->assign(fileName);
- return access(fileName, F_OK) == 0;
- }
- const char* pathList = getenv(pathListVar);
- if (pathList)
- {
- const char* pathStart = pathList;
- const char* pathEnd = pathStart;
- while (true)
- {
- while ((*pathEnd != ':') && (*pathEnd != 0))
- ++pathEnd;
- if (pathEnd > pathStart)
- {
- fullPath->assign(pathStart, pathEnd - pathStart);
- if ((*pathEnd != '/') && (*pathEnd != '\\'))
- *fullPath += '/';
- *fullPath += fileName;
- if (access(fullPath->c_str(), F_OK) == 0)
- return true;
- }
- if (*pathEnd == 0) // end explicitly so we don't access outside pathList.
- break;
- pathEnd++;
- pathStart = pathEnd;
- }
- }
- return false;
- }
- #endif // EA_PLATFORM_UNIX
- EASTDC_API bool SearchEnvironmentPath(const char16_t* pFileName, char16_t* pPath, const char16_t* pEnvironmentVar)
- {
- #if defined(EA_PLATFORM_WINDOWS) && EA_WINAPI_FAMILY_PARTITION(EA_WINAPI_PARTITION_DESKTOP)
- if(!pEnvironmentVar)
- pEnvironmentVar = EA_CHAR16("PATH");
- _wsearchenv(reinterpret_cast<const wchar_t*>(pFileName), reinterpret_cast<const wchar_t*>(pEnvironmentVar), reinterpret_cast<wchar_t*>(pPath));
- return (*pPath != 0);
- #else
- char8_t path8 [260];
- char8_t fileName8[260];
- Strlcpy(path8, pPath, 260);
- Strlcpy(fileName8, pFileName, 260);
- bool success;
- if (pEnvironmentVar)
- {
- char8_t environmentVariable8[260];
- Strlcpy(environmentVariable8, pEnvironmentVar, 260);
- success = EA::StdC::SearchEnvironmentPath(fileName8, path8, environmentVariable8);
- }
- else
- success = EA::StdC::SearchEnvironmentPath(fileName8, path8);
- Strlcpy(pPath, path8, 260);
- return success;
- #endif
- }
- EASTDC_API bool SearchEnvironmentPath(const char8_t* pFileName, char8_t* pPath, const char8_t* pEnvironmentVar)
- {
- #if defined(EA_PLATFORM_WINDOWS) && EA_WINAPI_FAMILY_PARTITION(EA_WINAPI_PARTITION_DESKTOP)
- if(!pEnvironmentVar)
- pEnvironmentVar = "PATH";
- _searchenv(pFileName, pEnvironmentVar, pPath);
- return (*pPath != 0);
- #elif defined(DISABLED_____EA_PLATFORM_UNIX) // Need to implement this in a way that doesn't use EASTL or an allocator.
- eastl::string8 path8(EASTLAllocatorType(UTFFOUNDATION_ALLOC_PREFIX "EAProcess"));
- bool success;
- if (pEnvironmentVar)
- success = SearchEnvPathWithMode(pEnvironmentVar, pFileName, F_OK, &path8); // Just require existence.
- else
- success = SearchEnvPathWithMode("PATH", pFileName, X_OK, &path8); // Require executability.
-
- if (success)
- {
- Strcpy(pPath, path8.c_str());
- return true;
- }
- return false;
- #else
- EA_UNUSED(pFileName);
- EA_UNUSED(pPath);
- EA_UNUSED(pEnvironmentVar);
- return false;
- #endif
- }
- #if defined(EA_PLATFORM_WINDOWS) && EA_WINAPI_FAMILY_PARTITION(EA_WINAPI_PARTITION_DESKTOP)
- namespace // anonymous namespace for this file.
- {
- typedef HINSTANCE (APIENTRY* ShellExecuteWFunctionType)(HWND hwnd, LPCWSTR lpOperation, LPCWSTR lpFile, LPCWSTR lpParameters, LPCWSTR lpDirectory, INT nShowCmd);
- static ShellExecuteWFunctionType ShellExecuteWFunction = NULL;
- static HINSTANCE hShellExecuteWFunctionLibrary = NULL;
- struct ShellExecuteWFunctionEntryPointFinder
- {
- ShellExecuteWFunctionEntryPointFinder()
- {
- hShellExecuteWFunctionLibrary = LoadLibraryW(EA_WCHAR("shell32.dll"));
- if(hShellExecuteWFunctionLibrary)
- ShellExecuteWFunction = (ShellExecuteWFunctionType)(void*)::GetProcAddress(hShellExecuteWFunctionLibrary, "ShellExecuteW");
- }
- ~ShellExecuteWFunctionEntryPointFinder()
- {
- if(hShellExecuteWFunctionLibrary)
- ::FreeLibrary(hShellExecuteWFunctionLibrary);
- }
- };
- }
- EASTDC_API bool OpenFile(const char16_t* pPath)
- {
- HINSTANCE hInstance = 0;
- ShellExecuteWFunctionEntryPointFinder sShellExecuteWFunctionEntryPointFinder;
- if(ShellExecuteWFunction)
- {
- if(Strstr(pPath, EA_CHAR16("http://")) == pPath) // If the path begins with "http://" and is thus a URL...
- {
- wchar_t pathMod[260 + 4];
- Strcpy(pathMod, EA_WCHAR("url:"));
- Strlcat(pathMod, reinterpret_cast<const wchar_t*>(pPath), 260 + 4); // ShellExecute wants the path to look like this: "url:http://www.bozo.com"
- hInstance = ShellExecuteWFunction(NULL, EA_WCHAR("open"), pathMod, NULL, NULL, SW_SHOWNORMAL);
- }
- else
- {
- hInstance = ShellExecuteWFunction(NULL, EA_WCHAR("open"), reinterpret_cast<const wchar_t*>(pPath), NULL, NULL, SW_SHOWNORMAL);
- }
- }
- return ((uintptr_t)hInstance > 32);
- }
- EASTDC_API bool OpenFile(const char8_t* pPath)
- {
- char16_t path16[260];
- Strlcpy(path16, pPath, 260);
- return OpenFile(path16);
- }
- #else
- EASTDC_API bool OpenFile(const char16_t* pPath)
- {
- char8_t path8[260];
- Strlcpy(path8, pPath, 260);
- return OpenFile(path8);
- }
- EASTDC_API bool OpenFile(const char8_t* pPath)
- {
- #if defined (EA_PLATFORM_OSX)
- const char8_t* args[] =
- {
- "open",
- pPath,
- 0
- };
- return Spawn("/usr/bin/open", args) != -1;
- #else
- EA_UNUSED(pPath);
- return false;
- #endif
- }
- #endif // EA_PLATFORM_WINDOWS
- } // namespace StdC
- } // namespace EA
|