cheatah
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> // _getpid
12#include <windows.h>
13#include <bcrypt.h> // BCryptGenRandom
14#else
15#include <unistd.h> // getpid
16#include <sys/random.h> // getentropy
17#endif
19// Compiled (non-template) symbols of the os module. Linked into a cheatah
20// executable only when the program `import os`s.
21namespace cheatah::os {
23namespace fs = std::filesystem;
25std::string getcwd() { return fs::current_path().string(); }
26void chdir(const std::string& path) { fs::current_path(path); }
28std::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;
36void mkdir(const std::string& path) { fs::create_directory(path); }
37void makedirs(const std::string& path) { fs::create_directories(path); }
38void rmdir(const std::string& path) { fs::remove(path); }
39bool remove(const std::string& path) { return fs::remove(path); }
40void rename(const std::string& src, const std::string& dst) { fs::rename(src, dst); }
42std::string getenv(const std::string& name, const std::string& fallback) {
43 const char* v = std::getenv(name.c_str());
44 return (v != nullptr) ? std::string(v) : fallback;
46void 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 overwrites
49 ::_putenv_s(name.c_str(), value.c_str());
50#else
51 ::setenv(name.c_str(), value.c_str(), overwrite ? 1 : 0);
52#endif
55#if defined(_WIN32)
56int getpid() { return static_cast<int>(::_getpid()); }
57#else
58int getpid() { return static_cast<int>(::getpid()); }
59#endif
60unsigned cpu_count() { return std::thread::hardware_concurrency(); }
61int system(const std::string& command) { return std::system(command.c_str()); }
63std::string urandom(int n) {
64 if (n < 0) throw std::invalid_argument("os.urandom: negative byte count");
65 std::string out(static_cast<std::size_t>(n), '\0');
66 if (n == 0) return out;
67#if defined(_WIN32)
68 if (::BCryptGenRandom(nullptr, reinterpret_cast<PUCHAR>(out.data()),
69 static_cast<ULONG>(out.size()),
70 BCRYPT_USE_SYSTEM_PREFERRED_RNG) != 0)
71 throw std::runtime_error("os.urandom: BCryptGenRandom failed");
72#else
73 // getentropy is limited to 256 bytes per call; loop for larger requests. A nonzero
74 // return means the OS could not supply randomness — fail loudly rather than hand back
75 // predictable bytes that a caller might turn into a key.
76 std::size_t off = 0;
77 while (off < out.size()) {
78 const std::size_t chunk = std::min<std::size_t>(out.size() - off, 256);
79 if (::getentropy(out.data() + off, chunk) != 0) throw std::runtime_error("os.urandom: getentropy failed");
80 off += chunk;
81 }
82#endif
83 return out;
86std::string module_ext() {
87#if defined(_WIN32)
88 return ".dll";
89#elif defined(__APPLE__)
90 return ".dylib";
91#else
92 return ".so";
93#endif
96namespace path {
98bool exists(const std::string& p) { return fs::exists(p); }
99bool isfile(const std::string& p) { return fs::is_regular_file(p); }
100bool isdir(const std::string& p) { return fs::is_directory(p); }
101std::string basename(const std::string& p) { return fs::path(p).filename().string(); }
102std::string dirname(const std::string& p) { return fs::path(p).parent_path().string(); }
103std::string abspath(const std::string& p) { return fs::absolute(p).string(); }
104std::string normpath(const std::string& p) { return fs::path(p).lexically_normal().string(); }
105std::uintmax_t getsize(const std::string& p) { return fs::file_size(p); }
107std::pair<std::string, std::string> splitext(const std::string& p) {
108 const std::string ext = fs::path(p).extension().string();
109 if (ext.empty()) {
110 return {p, ""};
111 }
112 return {p.substr(0, p.size() - ext.size()), ext};
115} // namespace path
116} // namespace cheatah::os