1
0
mirror of https://github.com/VCMP-SqMod/SqMod.git synced 2024-11-09 01:07:16 +01:00
SqMod/module/Library/System/Env.cpp
Sandu Liviu Catalin 68cbbdb831 Update Env.cpp
2021-04-20 19:24:44 +03:00

1278 lines
42 KiB
C++

// ------------------------------------------------------------------------------------------------
#include "Library/System/Env.hpp"
// ------------------------------------------------------------------------------------------------
#include <cctype>
#include <cstring>
// ------------------------------------------------------------------------------------------------
#ifdef SQMOD_OS_WINDOWS
#include <windows.h>
#include <shlobj.h>
#include <Poco/UnicodeConverter.h>
#else
#include <linux/limits.h>
#include <sys/utsname.h>
#include <unistd.h>
#include <pwd.h>
#endif
// ------------------------------------------------------------------------------------------------
namespace SqMod {
// ------------------------------------------------------------------------------------------------
#ifdef SQMOD_OS_WINDOWS
// Maximum path size in characters
#define SQMOD_MAX_PATH (sizeof(TCHAR) * MAX_PATH)
// Character to be used when working with path
typedef TCHAR PChar;
#else
// Maximum path size in characters
#define SQMOD_MAX_PATH (PATH_MAX)
// Character to be used when working with path
typedef CharT PChar;
#endif // SQMOD_OS_WINDOWS
// ------------------------------------------------------------------------------------------------
void SysEnv::Get(Buffer & b, const char * name, const char * fallback)
{
// Make sure the requested variable name is valid
if (name && *name != 0)
{
// Is there a buffer to work with?
if (!b)
{
// Acquire a moderately sized buffer
b = Buffer(128);
}
#ifdef SQMOD_OS_WINDOWS
// Retrieve the variable contents into the buffer that we have
DWORD len = GetEnvironmentVariableA(name, &b.Cursor(), b.Remaining());
// If the returned length is 0 then the variable doesn't exist
if (!len)
{
// Write the fall-back value into the buffer instead
len = b.WriteS(b.Position(), fallback);
}
// Did we have enough space left in the buffer?
else if (len > b.Remaining())
{
// Acquire a new buffer with a more appropriate capacity this time
b.Grow(len - b.Remaining() + 2);
// Attempt to retrieve the variable contents one more time
len = GetEnvironmentVariableA(name, &b.Cursor(), b.Remaining());
}
// Move the edit cursor to the end of the appended data
b.Advance(len);
#else
// Retrieve the pointer to the variable contents
const SQChar * val = getenv(name);
// If the returned pointer is null then the variable doesn't exist
if (!val)
{
// Write the fall-back value into the buffer instead
b.AppendS(fallback);
}
else
{
// Write the variable contents to the buffer
b.AppendS(val);
}
#endif
}
// Make sure that whatever string is in the buffer is null terminated
b.Cursor() = '\0';
}
// ------------------------------------------------------------------------------------------------
bool SysEnv::Has(const char * name)
{
#ifdef SQMOD_OS_WINDOWS
return (GetEnvironmentVariableA(name, nullptr, 0) > 0);
#else
return (getenv(name) != 0);
#endif
}
// ------------------------------------------------------------------------------------------------
bool SysEnv::Has(const String & name)
{
#ifdef SQMOD_OS_WINDOWS
return (GetEnvironmentVariableA(name.c_str(), nullptr, 0) > 0);
#else
return (getenv(name.c_str()) != 0);
#endif
}
// ------------------------------------------------------------------------------------------------
Buffer SysEnv::Get(const char * name, const char * fallback)
{
// Allocate a moderately sized buffer
Buffer b(128);
// Forward the call to the shared function
Get(b, name, fallback);
// Return ownership of the buffer
return b;
}
// ------------------------------------------------------------------------------------------------
bool SysEnv::Set(const char * name, const char * value)
{
#ifdef SQMOD_OS_WINDOWS
// Set the specified environment variable and return the result
return (SetEnvironmentVariableA(name, value) != 0);
#else
// Allocated a moderately sized buffer
Buffer b(256);
// Generate the necessary set command
b.WriteF(0, "%s=%s", name, value);
// Set the resulted environment variable and return the result
return (putenv(b.Data()) == 0);
#endif
}
// ------------------------------------------------------------------------------------------------
bool SysEnv::Set(const String & name, const String & value)
{
#ifdef SQMOD_OS_WINDOWS
// Set the specified environment variable and return the result
return (SetEnvironmentVariableA(name.c_str(), value.c_str()) != 0);
#else
// Obtain a temporary buffer capable of holding the set command
Buffer b(name.size() + value.size() + 2);
// Generate the necessary set command
b.WriteF(0, "%s=%s", name.c_str(), value.c_str());
// Set the resulted environment variable and return the result
return (putenv(b.Data()) == 0);
#endif
}
// ------------------------------------------------------------------------------------------------
String SysEnv::OSName()
{
#ifdef SQMOD_OS_WINDOWS
// Prepare the structure in which the OS information is retrieved
OSVERSIONINFO vi;
// Specify the size of the structure
vi.dwOSVersionInfoSize = sizeof(vi);
// Attempt to populate the previously created structure with information
if (GetVersionEx(&vi) == 0)
{
return "Unknown Windows";
}
// Identify the platform from the obtained information
switch (vi.dwPlatformId)
{
case VER_PLATFORM_WIN32s:
return "Windows 3.x";
case VER_PLATFORM_WIN32_WINDOWS:
return vi.dwMinorVersion == 0 ? "Windows 95" : "Windows 98";
case VER_PLATFORM_WIN32_NT:
return "Windows NT";
default:
return "Windows [Unknown]";
}
#else
// Prepare the structure in which the OS information is retrieved
struct utsname uts;
// Attempt to populate the previously created structure with information
if (uname(&uts) < 0)
{
return String("Unknown Unix");
}
// Return the requested information
return uts.sysname;
#endif
}
// ------------------------------------------------------------------------------------------------
String SysEnv::OSDisplayName()
{
#ifdef SQMOD_OS_WINDOWS
// Prepare the structure in which the OS information is retrieved
OSVERSIONINFO vi;
// Specify the size of the structure
vi.dwOSVersionInfoSize = sizeof(vi);
// Attempt to populate the previously created structure with information
if (GetVersionEx(&vi) == 0)
{
return "Unknown Windows";
}
// Identify the platform from the obtained information
switch(vi.dwMajorVersion) {
case 10:
switch (vi.dwMinorVersion) {
case 0: return "Windows 10/Windows Server 2016";
default: return "Windows 10.x [Unknown]";
}
case 6:
switch (vi.dwMinorVersion) {
case 0: return "Windows Vista/Server 2008";
case 1: return "Windows 7/Server 2008 R2";
case 2: return "Windows 8/Server 2012";
case 3: return "Windows 8.1/Server 2012 R2";
default: return "Windows 6.x [Unknown]";
}
// Even this is questionable
case 5:
switch (vi.dwMinorVersion) {
case 0: return "Windows 2000";
case 1: return "Windows XP";
case 2: return "Windows Server 2003/Windows Server 2003 R2";
default: return "Windows 5.x [Unknown]";
}
// This is here only for the lolz
case 4:
switch (vi.dwMinorVersion) {
case 0: return "Windows 95/Windows NT 4.0";
case 10: return "Windows 98";
case 90: return "Windows ME";
default: return "Windows 4.x [Unknown]";
}
// Something smells
default: return "Windows [Unknown]";
}
#else
// Use the same same output from OSName
return OSName();
#endif
}
// ------------------------------------------------------------------------------------------------
String SysEnv::OSVersion()
{
#ifdef SQMOD_OS_WINDOWS
// Prepare the structure in which the OS information is retrieved
OSVERSIONINFO vi;
// Specify the size of the structure
vi.dwOSVersionInfoSize = sizeof(vi);
// Attempt to populate the previously created structure with information
if (GetVersionEx(&vi) == 0)
{
String("Unknown");
}
// Obtain a temporary buffer capable of holding the version string
Buffer b(128);
// The amount of data written to the buffer
uint32_t sz;
// Generate the version string with the received information
if (vi.szCSDVersion[0])
{
sz = b.WriteF(0, "%lu.%lu (Build %lu : %s)",
vi.dwMajorVersion, vi.dwMinorVersion, vi.dwBuildNumber, vi.szCSDVersion);
}
else
{
sz = b.WriteF(0, "%lu.%lu (Build %lu)", vi.dwMajorVersion, vi.dwMinorVersion, vi.dwBuildNumber);
}
// Return a string with the buffer contents and leave the buffer clean after itself
return String(b.Get< String::value_type >(), sz);
#else
// Prepare the structure in which the OS information is retrieved
struct utsname uts;
// Attempt to populate the previously created structure with information
if (uname(&uts) < 0)
{
return String("Unknown");
}
// Return the requested information
return uts.release;
#endif
}
// ------------------------------------------------------------------------------------------------
String SysEnv::OSArchitecture()
{
#ifdef SQMOD_OS_WINDOWS
// Prepare the structure in which the system information is retrieved
SYSTEM_INFO si;
// Attempt to populate the previously created structure with information
GetSystemInfo(&si);
// Identify the architecture from the obtained information
switch (si.wProcessorArchitecture)
{
case PROCESSOR_ARCHITECTURE_INTEL: return "IA32";
case PROCESSOR_ARCHITECTURE_MIPS: return "MIPS";
case PROCESSOR_ARCHITECTURE_ALPHA: return "ALPHA";
case PROCESSOR_ARCHITECTURE_PPC: return "PPC";
case PROCESSOR_ARCHITECTURE_IA64: return "IA64";
#ifdef PROCESSOR_ARCHITECTURE_IA32_ON_WIN64
case PROCESSOR_ARCHITECTURE_IA32_ON_WIN64: return "IA64/32";
#endif
#ifdef PROCESSOR_ARCHITECTURE_AMD64
case PROCESSOR_ARCHITECTURE_AMD64: return "AMD64";
#endif
default: return "Unknown";
}
#else
// Prepare the structure in which the OS information is retrieved
struct utsname uts;
// Attempt to populate the previously created structure with information
if (uname(&uts) < 0)
{
return String("Unknown");
}
// Return the requested information
return uts.machine;
#endif
}
// ------------------------------------------------------------------------------------------------
String SysEnv::NodeName()
{
#ifdef SQMOD_OS_WINDOWS
// Obtain a temporary buffer capable of holding the node name string
Buffer b(MAX_COMPUTERNAME_LENGTH + 1);
// Used to tell the size of our buffer and the size of data written to it
DWORD size = b.Size< TCHAR >();
// Attempt to obtain the requested information
if (GetComputerNameA(b.Data(), &size) == 0)
{
return String();
}
// Return a string with the buffer contents and leave the buffer clean after itself
return String(b.Get< String::value_type >(), size);
#else
// Prepare the structure in which the OS information is retrieved
struct utsname uts;
// Attempt to populate the previously created structure with information
if (uname(&uts) < 0)
{
return String("Unknown");
}
// Return the requested information
return uts.nodename;
#endif
}
// ------------------------------------------------------------------------------------------------
uint32_t SysEnv::ProcessorCount()
{
#ifdef SQMOD_OS_WINDOWS
// Prepare the structure in which the system information is retrieved
SYSTEM_INFO si;
// Attempt to populate the previously created structure with information
GetSystemInfo(&si);
// Return the requested information
return si.dwNumberOfProcessors;
#elif defined(_SC_NPROCESSORS_ONLN)
// Attempt to obtain the number of processors available on the system
const int32_t count = sysconf(_SC_NPROCESSORS_ONLN);
// Validate the result and return the appropriate value
return (count < 0) ? 1 : static_cast< uint32_t >(count);
#else
// Obviously at least one processor should be available
return 1;
#endif
}
// ------------------------------------------------------------------------------------------------
void SysEnv::TerminatePath(Buffer & b)
{
// Is there any path to terminate?
if (!b)
{
return;
}
// Make sure that the path contains a trailing slash if necessary
else if (b.Cursor() == 0 && b.Before() != SQMOD_DIRSEP_CHAR)
{
b.Push(SQMOD_DIRSEP_CHAR);
}
// Make sure that whatever string is in the buffer, if any, is null terminated
b.Cursor() = '\0';
}
// ------------------------------------------------------------------------------------------------
void SysEnv::ExpandVars(Buffer & b, const char * pos, const char * end)
{
// Let's have a string to store the extracted variable name and value
String var;
// Extract the remaining directories from the specified path
while (pos != end)
{
// Should we start looking for a variable name?
if (*pos == '$')
{
// Clear previous name, if any
var.clear();
// Where the name of the variable starts and where it ends
const char * start = ++pos, * stop = pos;
// Is this variable name enclosed within curly braces?
if (*start == '{')
{
// Find the closing brace
stop = strchr(start, '}');
// Was there a closing brace?
if (!stop)
{
// Append the rest of the string to the buffer
b.AppendS(pos - 1, end - pos + 1);
// Stop parsing here
break;
}
// Is there anything between the brace?
else if ((stop - start) >= 1)
{
// Slice the variable name
var.assign(start + 1, stop - start - 1);
// Skip the ending brace
++stop;
}
}
// Is the dollar character followed by a character allowed in variable names?
else if (isalnum(*start) != 0 || *start == '_')
{
// Find the first character that isn't allowed in variable names
while (stop != end && (isalnum(*stop) != 0 || *stop == '_'))
{
++stop;
}
// Have we found anything?
if (start != stop)
{
// Slice the variable name
var.assign(start, stop - start);
}
}
else
{
// Just add the character to the buffer as is
b.Push('$');
// Skip to the next character
continue;
}
// Update the position
pos = stop;
// Do we have a valid variable name and does it exist?
if (!var.empty() && Has(var))
{
// Append the variable contents to our buffer
Get(b, var.c_str(), nullptr);
}
}
// Just add the character to the buffer as is
else
{
b.Push(*(pos++));
}
}
// Make sure the string in the buffer is null terminated
b.Cursor() = '\0';
}
// ------------------------------------------------------------------------------------------------
void SysEnv::ExpandPath(Buffer & b, const char * pos, const char * end)
{
// Does the path even contain something to be expanded?
if (pos == end || *pos == '\0')
{
return; // Nothing to expand!
}
// If the path starts with the tilde character then the home directory was requested
else if (*pos == '~')
{
// To be expanded, the tilde character must be followed by a slash
if (*(++pos) == SQMOD_DIRSEP_CHAR)
{
// Let's expand this tilde to the home directory
HomeDir(b);
// Let's skip the slash as well
++pos;
}
// Go back to the previous character and use it literally
else
{
--pos;
}
}
// The remaining string can be expanded normally
ExpandVars(b, pos, end);
}
// ------------------------------------------------------------------------------------------------
void SysEnv::ExpandVars(Buffer & b, const char * str)
{
// Do we have anything to expand?
if (!str || *str == '\0')
{
// Make sure the string in the specified buffer, if any, is null terminated
if (b)
{
b.Cursor() = '\0';
}
// Nothing to expand!
return;
}
// Calculate the size of the specified string
const auto len = static_cast< uint32_t >(strlen(str));
// Forward the call to the internal function
ExpandVars(b, str, str + len);
}
// ------------------------------------------------------------------------------------------------
void SysEnv::ExpandVars(Buffer & b, const String & str)
{
// Do we have anything to expand?
if (str.empty())
{
// Make sure the string in the specified buffer, if any, is null terminated
if (b)
{
b.Cursor() = '\0';
}
// Nothing to expand!
return;
}
// Forward the call to the internal function
ExpandVars(b, str.c_str(), str.c_str() + str.size());
}
// ------------------------------------------------------------------------------------------------
Buffer SysEnv::ExpandVars(const char * str)
{
// Do we have anything to expand?
if (!str || *str == '\0')
{
return Buffer(); // Nothing to expand!
}
// Calculate the size of the specified string
const auto len = static_cast< uint32_t >(strlen(str));
// Allocate a moderately sized buffer
Buffer b(len + 128);
// Forward the call to the internal function
ExpandVars(b, str, str + len);
// Return ownership of the buffer
return b;
}
// ------------------------------------------------------------------------------------------------
Buffer SysEnv::ExpandVars(const String & str)
{
// Do we have anything to expand?
if (str.empty())
{
return Buffer(); // Nothing to expand!
}
// Allocate a moderately sized buffer
Buffer b(static_cast< uint32_t >(str.size() + 128));
// Forward the call to the internal function
ExpandVars(b, str.c_str(), str.c_str() + str.size());
// Return ownership of the buffer
return b;
}
// ------------------------------------------------------------------------------------------------
void SysEnv::ExpandPath(Buffer & b, const char * path)
{
// Do we have anything to expand?
if (!path || *path == '\0')
{
// Make sure the string in the specified buffer, if any, is null terminated
if (b)
{
b.Cursor() = '\0';
}
// Nothing to expand!
return;
}
// Calculate the size of the specified string
const auto len = static_cast< uint32_t >(strlen(path));
// Forward the call to the internal function
ExpandPath(b, path, path + len);
}
// ------------------------------------------------------------------------------------------------
void SysEnv::ExpandPath(Buffer & b, const String & path)
{
// Do we have anything to expand?
if (path.empty())
{
// Make sure the string in the specified buffer, if any, is null terminated
if (b)
{
b.Cursor() = '\0';
}
// Nothing to expand!
return;
}
// Forward the call to the internal function
ExpandPath(b, path.c_str(), path.c_str() + path.size());
}
// ------------------------------------------------------------------------------------------------
Buffer SysEnv::ExpandPath(const char * path)
{
// Do we have anything to expand?
if (!path || *path == '\0')
{
return Buffer(); // Nothing to expand!
}
// Calculate the size of the specified string
const auto len = static_cast< uint32_t >(strlen(path));
// Allocate buffer capable of storing a full path
Buffer b(SQMOD_MAX_PATH);
// Forward the call to the internal function
ExpandPath(b, path, path + len);
// Return ownership of the buffer
return b;
}
// ------------------------------------------------------------------------------------------------
Buffer SysEnv::ExpandPath(const String & path)
{
// Do we have anything to expand?
if (path.empty())
{
return Buffer(); // Nothing to expand!
}
// Allocate buffer capable of storing a full path
Buffer b(SQMOD_MAX_PATH);
// Forward the call to the internal function
ExpandPath(b, path.c_str(), path.c_str() + path.size());
// Return ownership of the buffer
return b;
}
// ------------------------------------------------------------------------------------------------
void SysEnv::WorkingDir(Buffer & b)
{
#ifdef SQMOD_OS_WINDOWS
// Is there a buffer to work with?
if (!b)
{
// Allocate buffer capable of storing a full path
b = Buffer(SQMOD_MAX_PATH);
}
// Retrieve the current directory for the current process
DWORD len = GetCurrentDirectoryA(b.Remaining(), &b.Cursor());
// Did we have enough space left in the buffer?
if (len > b.Remaining())
{
// Acquire a new buffer with a more appropriate capacity this time
b.Grow(len - b.Remaining() + 2);
// Attempt to retrieve the working directory one more time
len = GetCurrentDirectoryA(b.Remaining(), &b.Cursor());
// ^ On failure the null terminator is included in the length
}
// Move the edit cursor to the end of the appended data
b.Advance(len);
#else
// Do we have enough space to store a full path?
if (b.Remaining() < SQMOD_MAX_PATH)
{
b.Grow(SQMOD_MAX_PATH - b.Remaining() + 2);
}
// Attempt to retrieve the current working directory and validate result
if (getcwd(&b.Cursor(), b.Remaining()))
{
// Move the edit cursor to the end of the appended data
b.Advance(strlen(&b.Cursor()));
}
#endif // SQMOD_OS_WINDOWS
// Make sure that the path is properly terminated
TerminatePath(b);
}
// ------------------------------------------------------------------------------------------------
Buffer SysEnv::WorkingDir()
{
// Allocate buffer capable of storing a full path
Buffer b(SQMOD_MAX_PATH);
// Forward the call to the regular function
WorkingDir(b);
// Return ownership of the buffer
return b;
}
// ------------------------------------------------------------------------------------------------
void SysEnv::HomeDir(Buffer & b)
{
#ifdef SQMOD_OS_WINDOWS
// Do we have enough space to store a full path?
if (b.Remaining() < SQMOD_MAX_PATH)
{
b.Grow(SQMOD_MAX_PATH - b.Remaining() + 2);
}
// Try the primary method of retrieving the home directory
if (SUCCEEDED(SHGetFolderPathA(nullptr, CSIDL_PROFILE, nullptr, 0, &b.Cursor())))
{
// Move the edit cursor to the end of the appended data
b.Advance(static_cast< uint32_t >(strlen(&b.Cursor())));
}
// Try the secondary method of retrieving the home directory
else if (Has("USERPROFILE"))
{
// Append the contents of the USERPROFILE environment variable
Get(b, "USERPROFILE", nullptr);
}
else if (Has("HOMEDRIVE") && Has("HOMEPATH"))
{
// Append the contents of the HOMEDRIVE environment variable
Get(b, "HOMEDRIVE", nullptr);
// Append the contents of the HOMEPATH environment variable
Get(b, "HOMEPATH", nullptr);
}
#else
// Try the primary method of retrieving the home directory
struct passwd * pwd = getpwuid(getuid());
// Validate the success of the previous operation
if (pwd)
{
// Append the path to our buffer
b.AppendS(pwd->pw_dir);
}
else
{
// Try the secondary method of retrieving the home directory
pwd = getpwuid(geteuid());
// Validate the success of the previous operation
if (pwd)
{
// Write the path to our buffer and store the size
b.AppendS(pwd->pw_dir);
}
// Fall back to the system environment variables
else if (Has("HOME"))
{
// Append the contents of the HOME environment variable
Get(b, "HOME", nullptr);
}
}
#endif // SQMOD_OS_WINDOWS
// Make sure that the path is properly terminated
TerminatePath(b);
}
// ------------------------------------------------------------------------------------------------
Buffer SysEnv::HomeDir()
{
// Allocate buffer capable of storing a full path
Buffer b(SQMOD_MAX_PATH);
// Forward the call to the regular function
HomeDir(b);
// Return ownership of the buffer
return b;
}
// ------------------------------------------------------------------------------------------------
void SysEnv::ConfigHomeDir(Buffer & b)
{
#ifdef SQMOD_OS_WINDOWS
// Is there a buffer to work with?
if (!b)
{
// Allocate buffer capable of storing a full path
b = Buffer(SQMOD_MAX_PATH);
}
// Does the APPDATA environment variable exist?
if (Has("APPDATA"))
{
// Obtain the contents of the APPDATA environment variable
Get(b, "APPDATA", nullptr);
}
else
{
// Default to the home directory
HomeDir(b);
}
#else
// Obtain the home directory path (should contain a trailing slash)
HomeDir(b);
// Use the home directory and append the ".config" sub folder
b.AppendS(".config");
#endif // SQMOD_OS_WINDOWS
// Make sure that the path is properly terminated
TerminatePath(b);
}
// ------------------------------------------------------------------------------------------------
Buffer SysEnv::ConfigHomeDir()
{
// Allocate buffer capable of storing a full path
Buffer b(SQMOD_MAX_PATH);
// Forward the call to the regular function
ConfigHomeDir(b);
// Return ownership of the buffer
return b;
}
// ------------------------------------------------------------------------------------------------
void SysEnv::DataHomeDir(Buffer & b)
{
#ifdef SQMOD_OS_WINDOWS
// Is there a buffer to work with?
if (!b)
{
// Allocate buffer capable of storing a full path
b = Buffer(SQMOD_MAX_PATH);
}
// Does the LOCALAPPDATA environment variable exist?
if (Has("LOCALAPPDATA"))
{
// Obtain the contents of the LOCALAPPDATA environment variable
return Get(b, "LOCALAPPDATA", nullptr);
}
// Default to the home config directory
return ConfigHomeDir(b);
#else
// Obtain the home directory path (should contain a trailing slash)
HomeDir(b);
// Use the home directory and append the ".local/share" sub folder
b.AppendS(".config/share");
// Make sure that the path is properly terminated
TerminatePath(b);
#endif // SQMOD_OS_WINDOWS
}
// ------------------------------------------------------------------------------------------------
Buffer SysEnv::DataHomeDir()
{
// Allocate buffer capable of storing a full path
Buffer b(SQMOD_MAX_PATH);
// Forward the call to the regular function
DataHomeDir(b);
// Return ownership of the buffer
return b;
}
// ------------------------------------------------------------------------------------------------
void SysEnv::TempHomeDir(Buffer & b)
{
#ifdef SQMOD_OS_WINDOWS
// Use the regular temp directory
TempDir(b);
#else
// Obtain the home directory path (should contain a trailing slash)
HomeDir(b);
// Use the home directory and append the ".local/tmp" folder
b.AppendS(".local/tmp");
// Make sure that the path is properly terminated
TerminatePath(b);
#endif // SQMOD_OS_WINDOWS
}
// ------------------------------------------------------------------------------------------------
Buffer SysEnv::TempHomeDir()
{
// Allocate buffer capable of storing a full path
Buffer b(SQMOD_MAX_PATH);
// Forward the call to the regular function
TempHomeDir(b);
// Return ownership of the buffer
return b;
}
// ------------------------------------------------------------------------------------------------
void SysEnv::CacheHomeDir(Buffer & b)
{
#ifdef SQMOD_OS_WINDOWS
// Use the regular temp directory
TempDir(b);
#else
// Obtain the home directory path (should contain a trailing slash)
HomeDir(b);
// Use the home directory and append the ".cache" folder
b.AppendS(".cache");
// Make sure that the path is properly terminated
TerminatePath(b);
#endif // SQMOD_OS_WINDOWS
}
// ------------------------------------------------------------------------------------------------
Buffer SysEnv::CacheHomeDir()
{
// Allocate buffer capable of storing a full path
Buffer b(SQMOD_MAX_PATH);
// Forward the call to the regular function
CacheHomeDir(b);
// Return ownership of the buffer
return b;
}
// ------------------------------------------------------------------------------------------------
void SysEnv::TempDir(Buffer & b)
{
#ifdef SQMOD_OS_WINDOWS
// Is there a buffer to work with?
if (!b)
{
// Allocate buffer capable of storing a full path
b = Buffer(SQMOD_MAX_PATH);
}
// Retrieve the path of the directory designated for temporary files
DWORD len = GetTempPathA(b.Remaining(), &b.Cursor());
// Did we failed to retrieve the path?
if (len == 0)
{
return; // Unable to retrieve the path!
}
// Did we have enough space left in the buffer?
else if (len > b.Remaining())
{
// Acquire a new buffer with a more appropriate capacity this time
b.Grow(len - b.Remaining() + 2);
// Attempt to retrieve the temporary directory one more time
/*len = */GetTempPathA(b.Remaining(), &b.Cursor());
// ^ On failure the null terminator is included in the length
}
// Convert the acquired path to its long form
len = GetLongPathNameA(&b.Cursor(), &b.Cursor(), b.Remaining());
// Did we failed to convert the path?
if (len == 0)
{
return; // Unable to convert the path!
}
// Did we have enough space left in the buffer?
else if (len > b.Remaining())
{
// Acquire a new buffer with a more appropriate capacity this time
b.Grow(len - b.Remaining() + 2);
// Attempt to retrieve the temporary directory again because we reused the buffer
GetTempPathA(b.Remaining(), &b.Cursor());
// Attempt to convert the acquired path to its long form one more time
len = GetLongPathNameA(&b.Cursor(), &b.Cursor(), b.Remaining());
// ^ On failure the null terminator is included in the length
}
// Move the edit cursor to the end of the appended data
b.Advance(len);
#else
// Does the TMPDIR environment variable exist?
if (SysEnv::Has("TMPDIR"))
{
// Obtain the contents of the TMPDIR environment variable
Get(b, "TMPDIR", nullptr);
}
else
{
// Default to the "/tmp" directory
b.AppendS("/tmp/");
}
#endif // SQMOD_OS_WINDOWS
// Make sure that the path is properly terminated
TerminatePath(b);
}
// ------------------------------------------------------------------------------------------------
Buffer SysEnv::TempDir()
{
// Allocate buffer capable of storing a full path
Buffer b(SQMOD_MAX_PATH);
// Forward the call to the regular function
TempDir(b);
// Return ownership of the buffer
return b;
}
// ------------------------------------------------------------------------------------------------
void SysEnv::ConfigDir(Buffer & b)
{
#ifdef SQMOD_OS_WINDOWS
// Is there a buffer to work with?
if (!b)
{
// Allocate buffer capable of storing a full path
b = Buffer(SQMOD_MAX_PATH);
}
// Does the PROGRAMDATA environment variable exist?
if (Has("PROGRAMDATA"))
{
// Obtain the contents of the PROGRAMDATA environment variable
Get(b, "PROGRAMDATA", nullptr);
}
else
{
// Make sure that whatever string is in the buffer, if any, is null terminated
b.Cursor() = '\0';
// Unable to retrieve the path!
return;
}
#else
// Default to "/etc" directory
b.AppendS("/etc/");
#endif // SQMOD_OS_WINDOWS
// Make sure that the path is properly terminated
TerminatePath(b);
}
// ------------------------------------------------------------------------------------------------
Buffer SysEnv::ConfigDir()
{
// Allocate buffer capable of storing a full path
Buffer b(SQMOD_MAX_PATH);
// Forward the call to the regular function
ConfigDir(b);
// Return ownership of the buffer
return b;
}
// ------------------------------------------------------------------------------------------------
void SysEnv::SystemDir(Buffer & b)
{
#ifdef SQMOD_OS_WINDOWS
// Is there a buffer to work with?
if (!b)
{
// Allocate buffer capable of storing a full path
b = Buffer(SQMOD_MAX_PATH);
}
// Retrieve the path of the system directory
DWORD len = GetSystemDirectoryA(&b.Cursor(), b.Remaining());
// Did we failed to retrieve the path?
if (len == 0)
{
return; // Unable to retrieve the path!
}
// Did we have enough space left in the buffer?
else if (len > b.Remaining())
{
// Acquire a new buffer with a more appropriate capacity this time
b.Grow(len - b.Remaining() + 2);
// Attempt to retrieve the path of the system directory one more time
len = GetSystemDirectoryA(&b.Cursor(), b.Remaining());
// ^ On failure the null terminator is included in the length
}
// Move the edit cursor to the end of the appended data
b.Advance(len);
#else
// Use a dummy directory for now
b.AppendS("/sys/");
#endif // SQMOD_OS_WINDOWS
// Make sure that the path is properly terminated
TerminatePath(b);
}
// ------------------------------------------------------------------------------------------------
Buffer SysEnv::SystemDir()
{
// Allocate buffer capable of storing a full path
Buffer b(SQMOD_MAX_PATH);
// Forward the call to the regular function
SystemDir(b);
// Return ownership of the buffer
return b;
}
// ------------------------------------------------------------------------------------------------
void SysEnv::NullDir(Buffer & b)
{
#ifdef SQMOD_OS_WINDOWS
b.AppendS("NUL:");
#else
b.AppendS("/dev/null/");
#endif // SQMOD_OS_WINDOWS
// Make sure that whatever string is in the buffer, if any, is null terminated
b.Cursor() = '\0';
}
// ------------------------------------------------------------------------------------------------
Buffer SysEnv::NullDir()
{
// Allocate buffer capable of storing a full path
Buffer b(SQMOD_MAX_PATH);
// Append the null path
#ifdef SQMOD_OS_WINDOWS
b.AppendS("NUL:");
#else
b.AppendS("/dev/null/");
#endif // SQMOD_OS_WINDOWS
// Make sure that whatever string is in the buffer, if any, is null terminated
b.Cursor() = '\0';
// Return ownership of the buffer
return b;
}
// ------------------------------------------------------------------------------------------------
static bool SqEnv_Has(const char * name)
{
return SysEnv::Has(name);
}
// ------------------------------------------------------------------------------------------------
static Object SqEnv_Get(const char * name)
{
return BufferToStrObj(SysEnv::Get(name, nullptr));
}
// ------------------------------------------------------------------------------------------------
static Object SqEnv_GetOr(const char * name, const char * fallback)
{
return BufferToStrObj(SysEnv::Get(name, fallback));
}
// ------------------------------------------------------------------------------------------------
static void SqEnv_Set(const char * name, const char * value)
{
SysEnv::Set(name, value);
}
// ------------------------------------------------------------------------------------------------
static Object SqEnv_ExpandVars(const char * str)
{
return BufferToStrObj(SysEnv::ExpandVars(str));
}
// ------------------------------------------------------------------------------------------------
static Object SqEnv_ExpandPath(const char * path)
{
return BufferToStrObj(SysEnv::ExpandPath(path));
}
// ------------------------------------------------------------------------------------------------
static Object SqEnv_WorkingDir()
{
return BufferToStrObj(SysEnv::WorkingDir());
}
// ------------------------------------------------------------------------------------------------
static Object SqEnv_HomeDir()
{
return BufferToStrObj(SysEnv::HomeDir());
}
// ------------------------------------------------------------------------------------------------
static Object SqEnv_ConfigHomeDir()
{
return BufferToStrObj(SysEnv::ConfigHomeDir());
}
// ------------------------------------------------------------------------------------------------
static Object SqEnv_DataHomeDir()
{
return BufferToStrObj(SysEnv::DataHomeDir());
}
// ------------------------------------------------------------------------------------------------
static Object SqEnv_TempHomeDir()
{
return BufferToStrObj(SysEnv::TempHomeDir());
}
// ------------------------------------------------------------------------------------------------
static Object SqEnv_CacheHomeDir()
{
return BufferToStrObj(SysEnv::CacheHomeDir());
}
// ------------------------------------------------------------------------------------------------
static Object SqEnv_TempDir()
{
return BufferToStrObj(SysEnv::TempDir());
}
// ------------------------------------------------------------------------------------------------
static Object SqEnv_ConfigDir()
{
return BufferToStrObj(SysEnv::ConfigDir());
}
// ------------------------------------------------------------------------------------------------
static Object SqEnv_SystemDir()
{
return BufferToStrObj(SysEnv::SystemDir());
}
// ------------------------------------------------------------------------------------------------
static Object SqEnv_NullDir()
{
return BufferToStrObj(SysEnv::NullDir());
}
// ------------------------------------------------------------------------------------------------
#ifdef SQMOD_OS_WINDOWS
#if !defined(WINVER) || (WINVER < 0x0600)
static void EnumerateLocales()
{
STHROWF("Windows version too old. Please set WINVER to a propper value when compiling the plug-in.");
}
#else
/* ------------------------------------------------------------------------------------------------
* Used internally to receive locales from the OS.
*/
static BOOL CALLBACK CallbackLocaleEx(LPWSTR pStr, DWORD SQ_UNUSED_ARG(dwFlags), LPARAM lparam) noexcept
{
// Retrieve the vector where we should append the received locale
auto loc_vec = reinterpret_cast< std::vector< std::string > * >(lparam);
// Create an empty string at the back of the vector
loc_vec->emplace_back();
// Convert the given wide-string to utf-8 into the created string
Poco::UnicodeConverter::convert(pStr, loc_vec->back());
// Continue
return TRUE;
}
/* ------------------------------------------------------------------------------------------------
* Retrieve a list of available locale names.
*/
static Array EnumerateLocales()
{
std::vector< std::string > loc_vec;
// Request a listing of available locales from the OS
EnumSystemLocalesEx(CallbackLocaleEx, LOCALE_ALL, reinterpret_cast< LPARAM >(&loc_vec), nullptr);
// Initialize an empty array
Array arr(SqVM(), 0);
// Did the OS give us any locales?
if (!loc_vec.empty()) {
// Reserve the array space upfront
arr.Reserve(static_cast< SQInteger >(loc_vec.size()));
// Append the elements from the vector into the array
arr.AppendFromCounted([&](HSQUIRRELVM vm, SQInteger i, size_t n) -> bool {
Var< std::string >::push(vm, loc_vec[static_cast< size_t >(i)]);
return static_cast< size_t >(i) < n;
}, loc_vec.size());
}
// Return the resulted array
return arr;
}
#endif
#else
/* ------------------------------------------------------------------------------------------------
* Unavailable. Try doing it via command line.
*/
static void EnumerateLocales()
{
STHROWF("This functionality is not available on linux because it cannot be done programmatically. Try using the command line: locale -a");
}
#endif
// ================================================================================================
void Register_SysEnv(HSQUIRRELVM vm)
{
Table sens(vm);
sens.Func(_SC("Has"), &SqEnv_Has);
sens.Func(_SC("Get"), &SqEnv_Get);
sens.Func(_SC("GetOr"), &SqEnv_GetOr);
sens.Func(_SC("Set"), &SqEnv_Set);
sens.Func(_SC("OSName"), &SysEnv::OSName);
sens.Func(_SC("OSDisplayName"), &SysEnv::OSDisplayName);
sens.Func(_SC("OSVersion"), &SysEnv::OSVersion);
sens.Func(_SC("OSArchitecture"), &SysEnv::OSArchitecture);
sens.Func(_SC("NodeName"), &SysEnv::NodeName);
sens.Func(_SC("ProcessorCount"), &SysEnv::ProcessorCount);
sens.Func(_SC("ExpandVars"), &SqEnv_ExpandVars);
sens.Func(_SC("ExpandPath"), &SqEnv_ExpandPath);
sens.Func(_SC("WorkingDir"), &SqEnv_WorkingDir);
sens.Func(_SC("HomeDir"), &SqEnv_HomeDir);
sens.Func(_SC("ConfigHomeDir"), &SqEnv_ConfigHomeDir);
sens.Func(_SC("DataHomeDir"), &SqEnv_DataHomeDir);
sens.Func(_SC("TempHomeDir"), &SqEnv_TempHomeDir);
sens.Func(_SC("CacheHomeDir"), &SqEnv_CacheHomeDir);
sens.Func(_SC("TempDir"), &SqEnv_TempDir);
sens.Func(_SC("ConfigDir"), &SqEnv_ConfigDir);
sens.Func(_SC("SystemDir"), &SqEnv_SystemDir);
sens.Func(_SC("NullDir"), &SqEnv_NullDir);
sens.Func(_SC("Locales"), &EnumerateLocales);
RootTable(vm).Bind(_SC("SqSysEnv"), sens);
}
} // Namespace:: SqMod