#include "pch.h"
#include "System/Environment.h"
#include <vector>
#include <map>
#include <sstream>
#if defined(_WIN32)
#include <windows.h>
#include <Psapi.h>
#include <shellapi.h>
#include <shlobj.h>
#include "Win32Internal.h"
#pragma comment(lib, "psapi.lib")
#pragma warning(disable : 4996)
using namespace DotNetDupe::System::Internal;
#else
#include <unistd.h>
#include <pwd.h>
#include <limits.h>
#include <sys/utsname.h>
#include <fstream>
#include <iostream>
extern char** environ;
#endif
namespace DotNetDupe {
namespace System {
String Environment::GetMachineName() {
#if defined(_WIN32)
wchar_t buffer [MAX_COMPUTERNAME_LENGTH + 1];
DWORD nSize = MAX_COMPUTERNAME_LENGTH + 1;
GetComputerNameW(buffer, &nSize);
return String(WCharToUtf8(buffer).c_str());
#else
char buffer[HOST_NAME_MAX + 1];
if (gethostname(buffer, sizeof(buffer)) == 0) {
return buffer;
}
return "";
#endif
}
String Environment::GetUserName() {
#if defined(_WIN32)
wchar_t buffer [256];
DWORD nSize = 256;
::GetUserNameW(buffer, &nSize);
return String(buffer);
#else
struct passwd *pPw = getpwuid(getuid());
if (pPw) {
return pPw->pw_name;
}
const char* pUser = getenv("USER");
return pUser ? pUser : "";
#endif
}
int Environment::GetProcessorCount() {
#if defined(_WIN32)
SYSTEM_INFO sysInfo;
GetSystemInfo(&sysInfo);
return (int)sysInfo.dwNumberOfProcessors;
#else
return (int)sysconf(_SC_NPROCESSORS_ONLN);
#endif
}
String Environment::GetNewLine() {
#if defined(_WIN32)
return "\r\n";
#else
return "\n";
#endif
}
String Environment::GetCurrentDirectory() {
#if defined(_WIN32)
wchar_t buffer [MAX_PATH];
::GetCurrentDirectoryW(MAX_PATH, buffer);
return String(WCharToUtf8(buffer).c_str());
#else
char buffer[PATH_MAX];
if (getcwd(buffer, sizeof(buffer))) {
return String(buffer);
}
return String("");
#endif
}
String Environment::GetSystemDirectory() {
#if defined(_WIN32)
wchar_t buffer [MAX_PATH];
::GetSystemDirectoryW(buffer, MAX_PATH);
return String(WCharToUtf8(buffer).c_str());
#else
return String("/usr/lib"); // Better POSIX default
#endif
}
String Environment::GetOSVersion() {
#if defined(_WIN32)
OSVERSIONINFOEXW info;
ZeroMemory(&info, sizeof(OSVERSIONINFOEXW));
info.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEXW);
GetVersionExW((LPOSVERSIONINFOW)&info);
std::stringstream ss;
ss << info.dwMajorVersion << "." << info.dwMinorVersion << "." << info.dwBuildNumber;
return String(ss.str().c_str());
#else
struct utsname name;
if (uname(&name) == 0) {
return String(name.release);
}
return String("Unknown");
#endif
}
String Environment::GetUserDomainName() {
return GetMachineName();
}
#if !defined(_WIN32)
static int64_t ReadLinuxWorkingSet() {
std::ifstream statusFile("/proc/self/status");
std::string sLine;
while (std::getline(statusFile, sLine)) {
if (sLine.compare(0, 6, "VmRSS:") == 0) {
size_t nStart = sLine.find_first_of("0123456789");
size_t nEnd = sLine.find_first_not_of("0123456789", nStart);
if (nStart != std::string::npos) return std::stoll(sLine.substr(nStart, nEnd - nStart)) * 1024;
}
}
return 0;
}
#endif
int64_t Environment::GetWorkingSet() {
#if defined(_WIN32)
PROCESS_MEMORY_COUNTERS pmc;
if (GetProcessMemoryInfo(GetCurrentProcess(), &pmc, sizeof(pmc))) return (int64_t)pmc.WorkingSetSize;
return 0;
#else
return ReadLinuxWorkingSet();
#endif
}
void Environment::Exit(int iExitCode) {
::exit(iExitCode);
}
String Environment::ExpandEnvironmentVariables(const String& sName) {
#if defined(_WIN32)
std::wstring sWName = Utf8ToWChar(sName.GetRawString());
DWORD nSize = ::ExpandEnvironmentStringsW(sWName.c_str(), NULL, 0);
if (nSize == 0) return sName;
std::vector<wchar_t> buffer(nSize);
::ExpandEnvironmentStringsW(sWName.c_str(), buffer.data(), nSize);
return String(WCharToUtf8(buffer.data()).c_str());
#else
if (sName.StartsWith("%", false) && sName.EndsWith("%", false)) {
const char* pVal = getenv(sName.Substring(1, sName.GetLength() - 2).GetRawString());
return pVal ? String(pVal) : sName;
}
return sName;
#endif
}
static std::vector<String> ReadCommandLineTokens() {
std::vector<String> tempArgs;
#if defined(_WIN32)
int nArgs;
LPWSTR* pSzArglist = CommandLineToArgvW(GetCommandLineW(), &nArgs);
if (pSzArglist != NULL) {
for (int iIndex = 0; iIndex < nArgs; iIndex++) tempArgs.push_back(String(WCharToUtf8(pSzArglist[iIndex]).c_str()));
LocalFree(pSzArglist);
}
#else
std::ifstream cmdline("/proc/self/cmdline", std::ios::binary);
std::string sArg;
while (std::getline(cmdline, sArg, '\0')) tempArgs.push_back(String(sArg.c_str()));
#endif
return tempArgs;
}
Array<String> Environment::GetCommandLineArgs() {
std::vector<String> tempArgs = ReadCommandLineTokens();
Array<String> result((int)tempArgs.size());
for (int iIndex = 0; iIndex < (int)tempArgs.size(); iIndex++) result[iIndex] = tempArgs[iIndex];
return result;
}
String Environment::GetEnvironmentVariable(const String& sVariable) {
#if defined(_WIN32)
std::wstring sWVar = Utf8ToWChar(sVariable.GetRawString());
DWORD nSize = ::GetEnvironmentVariableW(sWVar.c_str(), NULL, 0);
if (nSize == 0) return String("");
std::vector<wchar_t> buffer(nSize);
::GetEnvironmentVariableW(sWVar.c_str(), buffer.data(), nSize);
return String(WCharToUtf8(buffer.data()).c_str());
#else
const char* pVal = getenv(sVariable.GetRawString());
return pVal ? String(pVal) : String("");
#endif
}
static void ParseAndAddEnvEntry(const String& sLine, Collections::Generic::Dictionary<String, String>& result) {
int iEqIdx = sLine.IndexOf("=");
if (iEqIdx != -1) {
result.Add(sLine.Substring(0, iEqIdx), sLine.Substring(iEqIdx + 1, sLine.GetLength() - iEqIdx - 1));
}
}
Collections::Generic::Dictionary<String, String> Environment::GetEnvironmentVariables() {
Collections::Generic::Dictionary<String, String> result;
#if defined(_WIN32)
wchar_t* pLpvEnv = GetEnvironmentStringsW();
for (wchar_t* pLpszVariable = pLpvEnv; *pLpszVariable; pLpszVariable += wcslen(pLpszVariable) + 1) {
ParseAndAddEnvEntry(String(WCharToUtf8(pLpszVariable).c_str()), result);
}
FreeEnvironmentStringsW(pLpvEnv);
#else
for (char** ppEnv = environ; *ppEnv; ++ppEnv) {
ParseAndAddEnvEntry(String(*ppEnv), result);
}
#endif
return result;
}
#if defined(_WIN32)
static int GetCsidlForFolder(Environment::SpecialFolder eFolder) {
using SF = Environment::SpecialFolder;
switch (eFolder) {
case SF::ApplicationData: return CSIDL_APPDATA;
case SF::CommonApplicationData: return CSIDL_COMMON_APPDATA;
case SF::CommonProgramFiles: return CSIDL_PROGRAM_FILES_COMMON;
case SF::Cookies: return CSIDL_COOKIES;
case SF::Desktop: return CSIDL_DESKTOP;
case SF::Favorites: return CSIDL_FAVORITES;
case SF::History: return CSIDL_HISTORY;
case SF::InternetCache: return CSIDL_INTERNET_CACHE;
case SF::LocalApplicationData: return CSIDL_LOCAL_APPDATA;
case SF::MyComputer: return CSIDL_DRIVES;
case SF::MyDocuments: return CSIDL_MYDOCUMENTS;
case SF::MyMusic: return CSIDL_MYMUSIC;
case SF::MyPictures: return CSIDL_MYPICTURES;
case SF::MyVideos: return CSIDL_MYVIDEO;
case SF::ProgramFiles: return CSIDL_PROGRAM_FILES;
case SF::Programs: return CSIDL_PROGRAMS;
case SF::Recent: return CSIDL_RECENT;
case SF::SendTo: return CSIDL_SENDTO;
case SF::StartMenu: return CSIDL_STARTMENU;
case SF::Startup: return CSIDL_STARTUP;
case SF::System: return CSIDL_SYSTEM;
case SF::Templates: return CSIDL_TEMPLATES;
case SF::UserProfile: return CSIDL_PROFILE;
default: return 0;
}
}
#endif
String Environment::GetFolderPath(SpecialFolder eFolder) {
#if defined(_WIN32)
wchar_t buffer[MAX_PATH];
if (SHGetFolderPathW(NULL, GetCsidlForFolder(eFolder), NULL, 0, buffer) == S_OK) {
return String(WCharToUtf8(buffer).c_str());
}
return String("");
#else
if (eFolder == SpecialFolder::UserProfile || eFolder == SpecialFolder::MyDocuments) return String(getenv("HOME"));
if (eFolder == SpecialFolder::LocalApplicationData) return String(getenv("HOME")) + "/.local/share";
return String("");
#endif
}
Array<String> Environment::GetLogicalDrives() {
std::vector<String> tempDrives;
#if defined(_WIN32)
DWORD nDrives = ::GetLogicalDrives();
for (int iIndex = 0; iIndex < 26; iIndex++) {
if ((nDrives >> iIndex) & 1) {
char chDriveName[4] = { (char)('A' + iIndex), ':', '\\', 0 };
tempDrives.push_back(String(chDriveName));
}
}
#else
tempDrives.push_back(String("/"));
#endif
Array<String> result((int)tempDrives.size());
for (int iIndex = 0; iIndex < (int)tempDrives.size(); iIndex++) result[iIndex] = tempDrives[iIndex];
return result;
}
void Environment::SetEnvironmentVariable(const String& sVariable, const String& sValue) {
#if defined(_WIN32)
std::wstring sWVar = Utf8ToWChar(sVariable.GetRawString());
std::wstring sWVal = Utf8ToWChar(sValue.GetRawString());
::SetEnvironmentVariableW(sWVar.c_str(), sWVal.c_str());
#else
setenv(sVariable.GetRawString(), sValue.GetRawString(), 1);
#endif
}
OperatingSystem Environment::GetOperatingSystem() {
#if defined(_WIN32)
OSVERSIONINFOEXW info;
ZeroMemory(&info, sizeof(OSVERSIONINFOEXW));
info.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEXW);
GetVersionExW((LPOSVERSIONINFOW)&info);
return OperatingSystem(PlatformID::Win32NT, Version(info.dwMajorVersion, info.dwMinorVersion, info.dwBuildNumber, 0));
#else
struct utsname name;
uname(&name);
return OperatingSystem(PlatformID::Unix, Version(0, 0, 0, 0));
#endif
}
}
}