Source
stdlib/os/os.cpp
1
// Copyright (c) 2026 BigBrain LLC. MIT-licensed (see LICENSE).2
// Original work; see ACKNOWLEDGMENTS.md for the open-source ideas we build upon.3
#include "os.hpp"5
#include <algorithm>6
#include <cstdlib>7
#include <stdexcept>8
#include <thread>10
#if defined(_WIN32)11
#include <process.h> // _getpid12
#include <windows.h>13
#include <bcrypt.h> // BCryptGenRandom14
#else15
#include <unistd.h> // getpid16
#include <sys/random.h> // getentropy17
#endif19
// Compiled (non-template) symbols of the os module. Linked into a cheatah20
// executable only when the program `import os`s.21
namespace cheatah::os {23
namespace fs = std::filesystem;25
std::string getcwd() { return fs::current_path().string(); }26
void chdir(const std::string& path) { fs::current_path(path); }28
std::vector<std::string> listdir(const std::string& path) {29
std::vector<std::string> names;30
for (const auto& entry : fs::directory_iterator(path)) {31
names.push_back(entry.path().filename().string());32
}33
return names;34
}36
void mkdir(const std::string& path) { fs::create_directory(path); }37
void makedirs(const std::string& path) { fs::create_directories(path); }38
void rmdir(const std::string& path) { fs::remove(path); }39
bool remove(const std::string& path) { return fs::remove(path); }40
void rename(const std::string& src, const std::string& dst) { fs::rename(src, dst); }42
std::string getenv(const std::string& name, const std::string& fallback) {43
const char* v = std::getenv(name.c_str()); // NOLINT(concurrency-mt-unsafe): os.getenv IS the documented API; the same caveat as POSIX getenv applies44
return (v != nullptr) ? std::string(v) : fallback;45
}46
void setenv(const std::string& name, const std::string& value, bool overwrite) {47
#if defined(_WIN32)48
if (!overwrite && std::getenv(name.c_str()) != nullptr) return; // _putenv_s always overwrites49
::_putenv_s(name.c_str(), value.c_str());50
#else51
::setenv(name.c_str(), value.c_str(), overwrite ? 1 : 0); // NOLINT(concurrency-mt-unsafe): os.setenv IS the documented API; the same caveat as POSIX setenv applies52
#endif53
}55
#if defined(_WIN32)56
int getpid() { return static_cast<int>(::_getpid()); }57
#else58
int getpid() { return static_cast<int>(::getpid()); }59
#endif60
unsigned cpu_count() { return std::thread::hardware_concurrency(); }61
// NOLINT below: os.system IS the exposed shell-out API — the command processor is the62
// documented contract here, and the caller owns what they pass it (single-trust model).63
int system(const std::string& command) { return std::system(command.c_str()); } // NOLINT(cert-env33-c,concurrency-mt-unsafe)65
std::string urandom(int n) {66
if (n < 0) throw std::invalid_argument("os.urandom: negative byte count");67
std::string out(static_cast<std::size_t>(n), '\0');68
if (n == 0) return out;69
#if defined(_WIN32)70
if (::BCryptGenRandom(nullptr, reinterpret_cast<PUCHAR>(out.data()),71
static_cast<ULONG>(out.size()),72
BCRYPT_USE_SYSTEM_PREFERRED_RNG) != 0)73
throw std::runtime_error("os.urandom: BCryptGenRandom failed");74
#else75
// getentropy is limited to 256 bytes per call; loop for larger requests. A nonzero76
// return means the OS could not supply randomness — fail loudly rather than hand back77
// predictable bytes that a caller might turn into a key.78
std::size_t off = 0;79
while (off < out.size()) {80
const std::size_t chunk = std::min<std::size_t>(out.size() - off, 256);81
if (::getentropy(out.data() + off, chunk) != 0) throw std::runtime_error("os.urandom: getentropy failed");82
off += chunk;83
}84
#endif85
return out;86
}88
std::string module_ext() {89
#if defined(_WIN32)90
return ".dll";91
#elif defined(__APPLE__)92
return ".dylib";93
#else94
return ".so";95
#endif96
}98
namespace path {100
bool exists(const std::string& p) { return fs::exists(p); }101
bool isfile(const std::string& p) { return fs::is_regular_file(p); }102
bool isdir(const std::string& p) { return fs::is_directory(p); }103
std::string basename(const std::string& p) { return fs::path(p).filename().string(); }104
std::string dirname(const std::string& p) { return fs::path(p).parent_path().string(); }105
std::string abspath(const std::string& p) { return fs::absolute(p).string(); }106
std::string normpath(const std::string& p) { return fs::path(p).lexically_normal().string(); }107
std::uintmax_t getsize(const std::string& p) { return fs::file_size(p); }109
std::pair<std::string, std::string> splitext(const std::string& p) {110
const std::string ext = fs::path(p).extension().string();111
if (ext.empty()) {112
return {p, ""};113
}114
return {p.substr(0, p.size() - ext.size()), ext};115
}117
} // namespace path118
} // namespace cheatah::os