Files
EASTL/test/packages/EAStdC/source/EAProcess.cpp
T
jeanlemotan 48ab06b1d9 First
2024-07-02 18:10:39 +02:00

1076 lines
31 KiB
C++

///////////////////////////////////////////////////////////////////////////////
// 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 char* 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 char gCurrentProcessPath[kMaxPathLength] = { 0 };
#endif
EASTDC_API void SetCurrentProcessPath(const char* 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(char* 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(char* 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(char* 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);
char 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(char* 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);
char 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);
char 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(char* 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(char* 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);
char absolutePath[PATH_MAX];
if(realpath(pPath, absolutePath) != NULL) // Obtain canonicalized absolute pathname.
{
if(pathFlags & kPathFlagBundlePath)
{
// We recursively call dirname() until we find .extension
char 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);
char 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(char* 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(char* 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(char* 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);
char 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(char* 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);
char 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);
char 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(char* 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)
{
char 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(char* 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);
char 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);
char 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
char name8[260];
char 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 char* pName, char* 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 char* 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
char name8[260];
Strlcpy(name8, pName, 260);
char value8[260];
Strlcpy(value8, pValue, 260);
return SetEnvironmentVar(name8, value8);
#endif
}
EASTDC_API bool SetEnvironmentVar(const char* pName, const char* 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 char version
EA_FAIL_MESSAGE("Spawn: Not implemented for this platform.");
return -1;
#endif
}
EASTDC_API int Spawn(const char* pPath, const char* 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
char command8[260];
Strlcpy(command8, pCommand, 260);
return ExecuteShellCommand(command8);
#endif
}
int ExecuteShellCommand(const char* 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 char* pathListVar, const char* 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
char path8 [260];
char fileName8[260];
Strlcpy(path8, pPath, 260);
Strlcpy(fileName8, pFileName, 260);
bool success;
if (pEnvironmentVar)
{
char 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 char* pFileName, char* pPath, const char* 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 char* pPath)
{
char16_t path16[260];
Strlcpy(path16, pPath, 260);
return OpenFile(path16);
}
#else
EASTDC_API bool OpenFile(const char16_t* pPath)
{
char path8[260];
Strlcpy(path8, pPath, 260);
return OpenFile(path8);
}
EASTDC_API bool OpenFile(const char* pPath)
{
#if defined (EA_PLATFORM_OSX)
const char* 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