// ------------------------------------------------------------------------------------------------ #include "Library/System/Env.hpp" // ------------------------------------------------------------------------------------------------ #include #include // ------------------------------------------------------------------------------------------------ #ifdef SQMOD_OS_WINDOWS #include #include #include #else #include #include #include #include #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