31#include <mach-o/dyld.h>
44inline void setEnv(
const char* name,
const char* value,
bool replace =
true) {
46 if (value !=
nullptr) {
47 setenv(name, value, (
int) replace);
52 if (value !=
nullptr) {
53 _putenv_s(name, value);
63inline std::string
getEnv(
const char* name,
const std::string& fail =
"") {
64#if defined(_WIN32) || defined(_WIN64)
65 char* value =
nullptr;
67 errno_t err = _dupenv_s(&value, &len, name);
68 if (err != 0 || value ==
nullptr)
71 return std::string(value);
72#elif defined _GNU_SOURCE
77 char* output = secure_getenv(name);
80 return std::string(output);
82#warning "Failed to find a secure getenv() for your OS - please open an issue at https://github.com/LunarWatcher/stc if you know of an equivalent."
83 char* v = std::getenv(name);
84 if (v ==
nullptr)
return fail;
88 return std::string(v);
102 std::string rawPath = std::regex_replace(inputPath, std::regex(
"\\\\"),
"/");
107 if (rawPath.at(0) !=
'~')
110 std::optional<std::string> username;
111 std::string remainingPath;
116 std::string mod = rawPath.substr(1);
117 std::vector<std::string> pathSplit;
121 std::string token, cache;
123 token = mod.substr(0, pos);
125 cache.erase(0, pos + 1);
126 pathSplit.push_back(token);
127 pathSplit.push_back(cache);
130 if (rawPath.length() >= 2 && rawPath.at(1) !=
'/') {
133 username = pathSplit.at(0);
134 remainingPath = pathSplit.at(1);
135 }
else if (rawPath.find(
'/') != std::string::npos) {
138 remainingPath = pathSplit.at(1);
144 std::string homePath =
"";
145#if defined(_WIN32) || defined(_WIN64)
147 if (!username.has_value()) {
148 auto userProfile =
getEnv(
"USERPROFILE");
150 if (userProfile.empty()) {
151 auto homeDrive =
getEnv(
"HOMEDRIVE");
152 if (homeDrive.empty())
155 auto envHomePath =
getEnv(
"HOMEPATH");
157 if (envHomePath.empty()) {
158 throw std::runtime_error(
"Unable to find %HOMEPATH%. Specify the path explicitly instead.");
161 homePath = homeDrive + envHomePath;
163 homePath = userProfile;
166 throw std::runtime_error(
"This doesn't work."
167 " Due to Windows having a very limited API for expanding user paths, and it relies on environment "
168 "variables and assumptions, me (the developer), has decided to not implement ~user expansion on "
170 "I cannot easily test it, nor can I find any reassuring information for a universal pattern I can "
172 "Replace your path with an absolute path instead. An implementation for this feature may be "
173 "available in the future.");
177 homePath = std::regex_replace(homePath, std::regex(
"\\\\"),
"/");
196 struct passwd* passwdPtr =
nullptr;
198 if (!username.has_value()) {
204 passwdPtr = getpwuid(getuid());
206 auto& name = *username;
207 passwdPtr = getpwnam(name.c_str());
210 if (passwdPtr ==
nullptr) {
211 throw std::runtime_error(std::string(
"Failed to expand the user path for ") + rawPath +
". The system seems to think you don't exist. "
212 "Please specify the path to use - don't abbreviate it with ~.\n");
214 homePath = passwdPtr->pw_dir;
216 return std::filesystem::path{homePath} / remainingPath;
226 std::optional<std::string> username;
228 std::string homePath =
"";
229#if defined(_WIN32) || defined(_WIN64)
230 auto userProfile =
getEnv(
"USERPROFILE");
232 if (userProfile.empty()) {
233 auto homeDrive =
getEnv(
"HOMEDRIVE");
234 if (homeDrive.empty())
237 auto envHomePath =
getEnv(
"HOMEPATH");
239 if (envHomePath.empty()) {
240 throw std::runtime_error(
"Failed to find home path");
242 homePath = homeDrive + envHomePath;
244 homePath = userProfile;
247 homePath = std::regex_replace(homePath, std::regex(
"\\\\"),
"/");
266 struct passwd* passwdPtr =
nullptr;
280 passwdPtr = getpwuid(getuid());
282 if (passwdPtr ==
nullptr) {
283 throw std::runtime_error(
"Failed to find home directory");
285 homePath = passwdPtr->pw_dir;
287 return std::filesystem::path{homePath};
300inline std::string
syscommand(
const std::string& command,
int* codeOutput =
nullptr) {
301 std::array<char, 128> buffer;
304 std::unique_ptr<std::FILE, void(*)(FILE*)> fd {
305 popen(command.c_str(),
"r"),
307 std::ignore = pclose(fd);
311 throw std::runtime_error(
"Failed to run " + command);
314 while ((bytes = fread(
317 static_cast<int>(buffer.size()),
320 res.insert(res.end(), buffer.begin(), buffer.begin() + bytes);
323 if (codeOutput !=
nullptr) {
324 int r = pclose(fd.release());
325 int exitCode = WEXITSTATUS(r);
326 *codeOutput = exitCode;
344[[deprecated(
"Deprecated in favour of a better, fully integrated API. Switch to stc::Unix::Process. This method has vulnerabilities that will not be fixed")]]
345inline std::string
syscommand(std::vector<const char*> command,
int* codeOutput =
nullptr) {
346 command.push_back(
nullptr);
347 std::array<char, 256> buffer;
354 throw std::runtime_error(
"Failed to create pipe");
360 throw std::runtime_error(
"Fork error");
361 }
else if (pid == 0) {
364 dup2(fd[1], STDOUT_FILENO);
365 dup2(fd[1], STDERR_FILENO);
369 execv(command.at(0), (
char**) command.data());
374 while ((bytes = read(
376 buffer.data(), buffer.size()
378 res.insert(res.end(), buffer.begin(), buffer.begin() + bytes);
382 waitpid(pid, &status, 0);
384 if (codeOutput !=
nullptr) {
385 int exitCode = WEXITSTATUS(status);
386 *codeOutput = exitCode;
475[[deprecated(
"Deprecated in favour of a better, fully integrated API. Switch to stc::Unix::Process. This method has vulnerabilities that will not be fixed")]]
477 std::vector<const char*> command,
478 int* codeOutput =
nullptr
480 command.push_back(
nullptr);
485 throw std::runtime_error(
"Fork error");
486 }
else if (pid == 0) {
487 execv(command.at(0), (
char**) command.data());
491 waitpid(pid, &status, 0);
493 if (codeOutput !=
nullptr) {
494 int exitCode = WEXITSTATUS(status);
495 *codeOutput = exitCode;
509 constexpr size_t size = 256 + 2 ;
511 constexpr size_t size = MAX_COMPUTERNAME_LENGTH + 1;
516 int res = gethostname(hostname, size);
521 std::string str(hostname);
525 bool res = GetComputerNameEx(
526 ComputerNamePhysicalDnsHostname,
562 return _isatty(_fileno(streamDescriptor));
564 return isatty(fileno(streamDescriptor));
574 const size_t bufSize = PATH_MAX + 1;
575 char dirNameBuffer[bufSize];
576 uint32_t size = bufSize;
578 if (_NSGetExecutablePath(dirNameBuffer, &size) != 0) {
579 throw std::runtime_error(
"Crapple OS strikes again");
582 return std::filesystem::canonical(
591 return std::filesystem::canonical(
"/proc/self/exe");
596 std::vector<char> pathBuf;
599 pathBuf.resize(pathBuf.size() + MAX_PATH);
600 copied = GetModuleFileNameA(0, &pathBuf.at(0), pathBuf.size());
601 }
while(copied >= pathBuf.size());
602 pathBuf.resize(copied);
622template <
typename CharT>
624 if constexpr(std::is_same_v<CharT, char>) {
625 if (&ss == &std::cout) {
627 }
else if (&ss == &std::cerr) {
630 }
else if constexpr (std::is_same_v<CharT, wchar_t>){
631 if (&ss == &std::wcout) {
633 }
else if (&ss == &std::wcerr) {
643template <
typename CharT>
Definition CaptureStream.hpp:6
std::string executablePath()
Definition Environment.hpp:572
std::string getEnv(const char *name, const std::string &fail="")
Definition Environment.hpp:63
bool isCppStreamTTY(std::basic_ostream< CharT > &ss)
Definition Environment.hpp:644
void syscommandNoCapture(std::vector< const char * > command, int *codeOutput=nullptr)
Definition Environment.hpp:476
std::optional< std::string > getHostname()
Definition Environment.hpp:505
StreamType
Definition Environment.hpp:540
static constexpr StreamType getOutputStreamType(std::basic_ostream< CharT > &ss)
Definition Environment.hpp:623
std::filesystem::path expandUserPath(const std::string &inputPath)
Definition Environment.hpp:100
std::filesystem::path getHome()
Definition Environment.hpp:224
bool isStreamTTY(StreamType type=StreamType::STDOUT)
Definition Environment.hpp:555
std::string syscommand(const std::string &command, int *codeOutput=nullptr)
Definition Environment.hpp:300
void setEnv(const char *name, const char *value, bool replace=true)
Definition Environment.hpp:44