os.h
cpp
#ifndef LIBSCL_OS_H_
#define LIBSCL_OS_H_
//////////////////////////////////////////////////////////////////////////
#include "base.h"
//////////////////////////////////////////////////////////////////////////
namespace scl::os {
//////////////////////////////////////////////////////////////////////////
int last_errno() noexcept;
errco_t last_error() noexcept;
errco_t errno_cast(int err_code) noexcept;
//////////////////////////////////////////////////////////////////////////
std::string sys_name() noexcept;
std::string sys_version() noexcept;
std::string sys_arch() noexcept;
size_t cpu_cores() noexcept;
struct MemoryStatus {
uint64_t total_bytes = 0;
uint64_t free_bytes = 0;
int32_t free_percent = 0;
int32_t used_percent = 0;
};
MemoryStatus mem_status() noexcept;
std::string cpu_info() noexcept;
std::string sys_info() noexcept;
std::string time_info() noexcept;
std::string mem_info() noexcept;
//////////////////////////////////////////////////////////////////////////
bool set_cpu_affinity(int64_t thread_id, const std::vector<int32_t> &cpu_index);
//////////////////////////////////////////////////////////////////////////
bool is_exists(const std::string_view &path, errco_t *err = nullptr) noexcept;
bool is_dir(const std::string_view &path, errco_t *err = nullptr) noexcept;
bool is_file(const std::string_view &path, errco_t *err = nullptr) noexcept;
bool is_empty(const std::string_view &path, errco_t *err = nullptr) noexcept;
errco_t check(const std::string_view &path) noexcept;
errco_t setwd(const std::string_view &path) noexcept;
std::string getwd() noexcept;
std::string temp_dir(const std::string_view &more) noexcept;
std::string home_dir(const std::string_view &more) noexcept;
errco_t mkdir(const std::string_view &path) noexcept;
errco_t mkdir_all(const std::string_view &path) noexcept;
errco_t remove(const std::string_view &path) noexcept;
errco_t remove_all(const std::string_view &path) noexcept;
errco_t rename(const std::string_view &old_name, const std::string_view &new_name) noexcept;
errco_t copy_file(const std::string_view &from, const std::string_view &to) noexcept;
errco_t update_file(const std::string_view &from, const std::string_view &to) noexcept;
errco_t copy_dir(const std::string_view &from, const std::string_view &to) noexcept;
errco_t update_dir(const std::string_view &from, const std::string_view &to) noexcept;
//////////////////////////////////////////////////////////////////////////
errco_t file_size(const std::string_view &path, uint64_t *size) noexcept;
//////////////////////////////////////////////////////////////////////////
/// nanoseconds
struct FileTime {
uint64_t atime = 0;
uint64_t mtime = 0;
uint64_t ctime = 0;
};
errco_t file_time(const std::string &path, FileTime *time) noexcept;
errco_t set_file_time(const std::string &path, const FileTime &time) noexcept;
//////////////////////////////////////////////////////////////////////////
enum FileMode : uint32_t {
// unix-like
OWNER_R = 0000'0400, // R for owner
OWNER_W = 0000'0200, // W for owner
OWNER_X = 0000'0100, // X for owner
OWNER_A = 0000'0700, // RWX mask for owner
GROUP_R = 0000'0040, // R for group
GROUP_W = 0000'0020, // W for group
GROUP_X = 0000'0010, // X for group
GROUP_A = 0000'0070, // RWX mask for group
OTHER_R = 0000'0004, // R for other
OTHER_W = 0000'0002, // W for other
OTHER_X = 0000'0001, // X for other
OTHER_A = 0000'0007, // RWX mask for other
// windows
READONLY = 0001'0000, // readonly attribute
HIDDEN = 0002'0000, // hidden attribute
DIRECTORY = 0004'0000, // directory attribute
};
errco_t file_mode(const std::string &path, uint32_t *mode) noexcept;
errco_t set_file_mode(const std::string &path, uint32_t mode) noexcept;
// alias, same with set_file_mode
errco_t chmod(const std::string &path, uint32_t mode) noexcept;
//////////////////////////////////////////////////////////////////////////
struct FileInfo {
bool is_exists = false;
bool is_dir = false;
uint64_t size = 0;
FileTime times;
uint32_t modes;
};
errco_t file_info(const std::string &path, FileInfo *info) noexcept;
//////////////////////////////////////////////////////////////////////////
/// @brief
/// @param dir
/// @param bool on_dir(const std::string &item, bool is_dir, uint64_t size);
/// @return
errco_t enum_dir(const std::string_view &dir, const std::function<bool(const std::string &, bool, uint64_t)> &func) noexcept;
errco_t recursive_dir(const std::string_view &dir, const std::function<bool(const std::string &, bool, uint64_t)> &func) noexcept;
//////////////////////////////////////////////////////////////////////////
errco_t read_file(const std::string &path, std::string *data) noexcept;
errco_t write_file(const std::string &path, const std::string &data, bool trunc = true) noexcept;
// read_txt_file
//
// encoding:
// u8: UTF-8, no BOM.
// u16l: UTF-16 Little-Endian, no BOM.
// u16b: UTF-16 Big-Endian, no BOM.
errco_t read_txt_file(const std::string &path, std::string *data, std::string *encoding) noexcept;
errco_t read_txt_file(const std::string &path, std::vector<std::string> *data, std::string *encoding) noexcept;
// write_txt_file
//
// encoding:
// u8: UTF-8, no BOM.
// u8b: UTF-8, with BOM.
// u16l: UTF-16 Little-Endian, no BOM.
// u16lb: UTF-16 Little-Endian, with BOM.
// u16b: UTF-16 Big-Endian, no BOM.
// u16bb: UTF-16 Big-Endian, with BOM.
errco_t write_txt_file(const std::string &path, const std::string &data, const std::string &encoding = "u8") noexcept;
errco_t create_null_file(const std::string &path, uint64_t mb_bytes);
//////////////////////////////////////////////////////////////////////////
} // namespace scl::os
//////////////////////////////////////////////////////////////////////////
#endif // !LIBSCL_OS_H_
os.cpp
cpp
#include "os.h"
#include "time.h"
#include "strings.h"
#include "format.h"
#include <filesystem>
#include <fstream>
#include <sstream>
#include <iomanip>
#ifdef WIN32
#include <windows.h>
#include <shlobj.h>
#include <map>
#else
#include <sys/utsname.h>
#include <sys/sysinfo.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/time.h>
#include <unistd.h>
#include <pwd.h>
#endif // WIN32
//////////////////////////////////////////////////////////////////////////
namespace scl::os {
//////////////////////////////////////////////////////////////////////////
static std::string s_sys_name;
static std::string s_sys_version;
static std::string s_sys_arch;
static size_t s_cpu_cores = 0;
static std::string s_cpu_info;
//////////////////////////////////////////////////////////////////////////
#ifdef WIN32
bool rtl_get_version(POSVERSIONINFOW posvi) {
NTSTATUS(NTAPI * RtlGetVersion)
(PRTL_OSVERSIONINFOW lpVersionInformation) = nullptr;
HMODULE ntdll = GetModuleHandleW(L"ntdll.dll");
if (ntdll == nullptr) {
return false;
}
*reinterpret_cast<FARPROC *>(&RtlGetVersion) = GetProcAddress(ntdll, "RtlGetVersion");
if (RtlGetVersion == nullptr) {
return false;
}
return (RtlGetVersion(posvi) >= 0);
}
std::string rtl_get_version() {
std::string result = "Windows";
OSVERSIONINFOEXW osvi = {0};
osvi.dwOSVersionInfoSize = sizeof(osvi);
if (!rtl_get_version(reinterpret_cast<POSVERSIONINFOW>(&osvi))) {
return result;
}
bool is_server = false;
if (osvi.wProductType != VER_NT_WORKSTATION) {
is_server = true;
}
std::map<DWORD, std::string> win10_build_map = {
{22621, " 11 version 22H2"},
{22000, " 11 version 21H2"},
{19045, " 10 version 22H2"},
{19044, " 10 version 21H2"},
{19043, " 10 version 21H1"},
{19042, " 10 version 20H2"},
{19041, " 10 version 2004"},
{18363, " 10 version 1909"},
{18362, " 10 version 1903"},
{17763, " 10 version 1809"},
{17134, " 10 version 1803"},
{16299, " 10 version 1709"},
{15063, " 10 version 1703"},
{14393, " 10 version 1607"},
{10586, " 10 version 1511"},
{10240, " 10 version 1507"}};
std::map<DWORD, std::string> win10_server_build_map = {
{20348, " Server 2022 version 21H2"},
{19042, " Server, version 20H2"},
{19041, " Server, version 2004"},
{18363, " Server, version 1909"},
{18362, " Server, version 1903"},
{17763, " Server 2019, version 1809"},
{17134, " Server, version 1803"},
{16299, " Server, version 1709"},
{14393, " Server 2016, version 1607"}};
if (osvi.dwMajorVersion == 10) { // Window 10+, Windows Server 2016+
if (!is_server) {
auto it = win10_build_map.find(osvi.dwBuildNumber);
if (it != win10_build_map.end()) {
result.append(it->second);
} else if (osvi.dwBuildNumber >= 22000) {
result.append(" 11");
} else {
result.append(" 10");
}
} else {
auto it = win10_server_build_map.find(osvi.dwBuildNumber);
if (it != win10_server_build_map.end()) {
result.append(it->second);
} else if (osvi.dwBuildNumber >= 20348) {
result.append(" Server 2022");
} else if (osvi.dwBuildNumber >= 17763) {
result.append(" Server 2019");
} else {
result.append(" Server 2016");
}
}
} else if (osvi.dwMajorVersion == 6) { // Windows Vista+, Windows Server 2008+
switch (osvi.dwMinorVersion) {
case 3: {
result.append(is_server ? " Server 2012 R2" : " 8.1");
break;
}
case 2: {
result.append(is_server ? " Server 2012" : " 8");
break;
}
case 1: {
result.append(is_server ? " Server 2008 R2" : " 7");
break;
}
case 0: {
result.append(is_server ? " Server 2008" : " Vista");
break;
}
}
} else if (osvi.dwMajorVersion == 5) { // Windows 2000+, Windows Server 2003+
switch (osvi.dwMinorVersion) {
case 2: {
result.append(is_server ? " Server 2003" : " XP x64");
break;
}
case 1: {
result.append(is_server ? " " : " XP");
break;
}
case 0: {
result.append(is_server ? " 2000 Server" : " 2000");
break;
}
}
}
std::string szCSDVersion = strings::u16s2u8s(osvi.szCSDVersion);
if (!szCSDVersion.empty()) {
result.append(" " + szCSDVersion);
}
if (osvi.dwBuildNumber < 10) {
result.append(", " + std::to_string(osvi.dwMajorVersion) + "." + std::to_string(osvi.dwMinorVersion));
result.append(" " + std::to_string(osvi.dwBuildNumber));
} else {
result.append(", " + std::to_string(osvi.dwBuildNumber));
}
return result;
}
std::string get_cpu_info() {
std::string result = "unknow";
HKEY key;
LONG retval = ::RegOpenKeyExA(HKEY_LOCAL_MACHINE, "HARDWARE\\DESCRIPTION\\System\\CentralProcessor\\0", 0, KEY_READ, &key);
if (retval == ERROR_SUCCESS) {
DWORD key_type = 0;
char key_value[1024] = {0};
DWORD key_size = sizeof(key_value);
retval = RegQueryValueEx(key, "ProcessorNameString", NULL, &key_type, reinterpret_cast<PBYTE>(key_value), &key_size);
if (retval == ERROR_SUCCESS && key_type == REG_SZ) {
result = key_value;
}
::RegCloseKey(key);
}
return result;
}
#else
std::string get_os_release() {
std::string result = "unknow";
std::ifstream ifs("/etc/os-release");
if (!ifs.is_open()) {
return result;
}
std::string line;
while (std::getline(ifs, line)) {
if (line.find("NAME=") != line.npos) {
result = line.substr(line.find("\"") + 1);
result.pop_back();
result.append(" ");
continue;
} else if (line.find("VERSION=") != line.npos) {
result.append(line.substr(line.find("\"") + 1));
result.pop_back();
break;
}
}
ifs.close();
return result;
}
std::string get_lsb_release() {
std::string result = "unknow";
std::FILE *fp = ::popen("lsb_release -a 2> /dev/null", "r");
if (fp == nullptr) {
return result;
}
char buffer[4096];
while (std::fgets(buffer, sizeof(buffer), fp) != nullptr) {
std::string line(buffer);
if (line.find("Description:") != line.npos) {
result = line.substr(line.find(":") + 1);
result = result.substr(result.find_first_not_of(" \t\n\r\f"));
result = result.substr(0, result.find_last_not_of(" \t\n\r\f") + 1);
break;
}
}
::pclose(fp);
return result;
}
std::string get_cpu_info() {
std::string result = "unknow";
std::ifstream ifs("/proc/cpuinfo");
if (!ifs.is_open()) {
return result;
}
std::string line;
while (std::getline(ifs, line)) {
if (line.find("model name") != line.npos) {
result = line.substr(line.find(":") + 2);
break;
} else if (line.find("Hardware") != line.npos) {
result = line.substr(line.find(":") + 2);
break;
}
}
ifs.close();
return result;
}
#endif // !WIN32
//////////////////////////////////////////////////////////////////////////
bool s_init_status = []() -> bool {
#ifdef WIN32
s_sys_name = "Windows";
s_sys_version = rtl_get_version();
SYSTEM_INFO si = {0};
::GetSystemInfo(&si);
switch (si.wProcessorArchitecture) {
case PROCESSOR_ARCHITECTURE_INTEL: {
s_sys_arch = "x86";
break;
}
case PROCESSOR_ARCHITECTURE_AMD64: {
s_sys_arch = "amd64";
break;
}
case PROCESSOR_ARCHITECTURE_ARM64: {
s_sys_arch = "arm64";
break;
}
}
s_cpu_cores = si.dwNumberOfProcessors;
s_cpu_info = get_cpu_info();
#else
struct utsname uts;
::uname(&uts);
s_sys_name = uts.sysname;
s_sys_version = get_os_release();
if (s_sys_version == "unknow") {
s_sys_version = get_lsb_release();
}
if (s_sys_version == "unknow") {
s_sys_version = uts.version;
}
s_sys_arch = uts.machine;
if (s_sys_arch == "x86_64") {
s_sys_arch = "amd64";
} else if (s_sys_arch == "aarch64") {
s_sys_arch = "arm64";
}
s_cpu_cores = ::sysconf(_SC_NPROCESSORS_ONLN);
s_cpu_info = get_cpu_info();
#endif // !WIN32
return true;
}();
//////////////////////////////////////////////////////////////////////////
int last_errno() noexcept {
#ifdef WIN32
return static_cast<int>(::GetLastError());
#else
return errno;
#endif // WIN32
}
errco_t last_error() noexcept {
#ifdef WIN32
DWORD err_code = ::GetLastError();
std::wstring err_msg;
if (err_code != 0) {
PWSTR pbuff = nullptr;
size_t size = ::FormatMessageW(
FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
nullptr,
err_code,
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
reinterpret_cast<PWSTR>(&pbuff),
0,
nullptr);
err_msg.assign(pbuff, size);
::LocalFree(pbuff);
}
return errco_t(static_cast<int>(err_code), strings::u16s2u8s(err_msg));
#else
int err_code = errno;
char err_msg[1024] = {0};
char *unused = ::strerror_r(err_code, err_msg, sizeof(err_msg));
return errco_t(err_code, err_msg);
#endif // WIN32
}
errco_t errno_cast(int err_code) noexcept {
#ifdef WIN32
std::wstring err_msg;
if (err_code != 0) {
PWSTR pbuff = nullptr;
size_t size = ::FormatMessageW(
FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
nullptr,
static_cast<DWORD>(err_code),
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
reinterpret_cast<PWSTR>(&pbuff),
0,
nullptr);
err_msg.assign(pbuff, size);
::LocalFree(pbuff);
}
return errco_t(err_code, strings::u16s2u8s(err_msg));
#else
char err_msg[1024] = {0};
char *unused = ::strerror_r(err_code, err_msg, sizeof(err_msg));
return errco_t(err_code, err_msg);
#endif // WIN32
}
//////////////////////////////////////////////////////////////////////////
std::string sys_name() noexcept {
return s_sys_name;
}
std::string sys_version() noexcept {
return s_sys_version;
}
std::string sys_arch() noexcept {
return s_sys_arch;
}
size_t cpu_cores() noexcept {
return s_cpu_cores;
}
MemoryStatus mem_status() noexcept {
MemoryStatus ms = {0};
#ifdef WIN32
MEMORYSTATUSEX msex = {0};
msex.dwLength = sizeof(msex);
::GlobalMemoryStatusEx(&msex);
ms.total_bytes = msex.ullTotalPhys;
ms.free_bytes = msex.ullAvailPhys;
#else
struct sysinfo si = {0};
::sysinfo(&si);
ms.total_bytes = si.totalram * si.mem_unit;
ms.free_bytes = si.freeram * si.mem_unit;
#endif // !WIN32
ms.free_percent = static_cast<int32_t>(ms.free_bytes * 100 / ms.total_bytes);
ms.used_percent = 100 - ms.free_percent;
return ms;
}
std::string cpu_info() noexcept {
return s_cpu_info;
}
std::string sys_info() noexcept {
std::ostringstream oss;
oss << s_sys_name << ", "
<< s_sys_version << ", "
<< s_sys_arch << ", "
<< "cpu cores: " << s_cpu_cores;
return oss.str();
}
std::string time_info() noexcept {
std::ostringstream oss;
oss << "Local Time: " << time::format_local("%Y-%m-%d %H:%M:%S", time::now()) << ", "
<< "Time Zone: (UTC, " << std::showpos << std::internal << std::setw(3) << std::setfill('0') << time::tz_offset_hour << "00), "
<< "Clock nanosecond accuracy: " << clock::nsec_accuracy();
return oss.str();
}
std::string mem_info() noexcept {
MemoryStatus ms = mem_status();
std::ostringstream oss;
oss << "Memory used: " << ms.used_percent << "%, "
<< "free: " << format_bytes_unit(ms.free_bytes) << ", "
<< "total: " << format_bytes_unit(ms.total_bytes);
return oss.str();
}
//////////////////////////////////////////////////////////////////////////
bool set_cpu_affinity(int64_t thread_id, const std::vector<int32_t> &cpu_index) {
if (cpu_index.empty()) {
return false;
}
#ifdef WIN32
DWORD_PTR result_mask = 0;
for (auto &v : cpu_index) {
if (v < 0) {
continue;
}
DWORD_PTR mask = (1ull << v);
result_mask |= mask;
}
if (result_mask == 0) {
return false;
}
auto handle = ::OpenThread(THREAD_SET_INFORMATION | THREAD_QUERY_INFORMATION, FALSE, static_cast<DWORD>(thread_id));
if (handle == nullptr) {
return false;
}
defer(::CloseHandle(handle));
return ::SetThreadAffinityMask(handle, result_mask) != 0;
#else
cpu_set_t cpuset;
CPU_ZERO(&cpuset);
size_t count = 0;
for (auto &v : cpu_index) {
if (v < 0) {
continue;
}
CPU_SET(v, &cpuset);
++count;
}
if (count == 0) {
return false;
}
#ifdef __ANDROID__
return ::sched_setaffinity(thread_id, sizeof(cpu_set_t), &cpuset);
#else
return ::pthread_setaffinity_np(thread_id, sizeof(cpuset), &cpuset) == 0;
#endif // __ANDROID__
#endif // WIN32
}
//////////////////////////////////////////////////////////////////////////
namespace fs = std::filesystem;
//////////////////////////////////////////////////////////////////////////
bool is_exists(const std::string_view &path, errco_t *err /*= nullptr*/) noexcept {
std::error_code ec;
bool ok = fs::exists(path, ec);
if (err != nullptr) {
*err = ec;
}
return ok;
}
bool is_dir(const std::string_view &path, errco_t *err /*= nullptr*/) noexcept {
std::error_code ec;
bool ok = fs::is_directory(path, ec);
if (err != nullptr) {
*err = ec;
}
return ok;
}
bool is_file(const std::string_view &path, errco_t *err /*= nullptr*/) noexcept {
std::error_code ec;
bool ok = fs::is_regular_file(path, ec);
if (err != nullptr) {
*err = ec;
}
return ok;
}
bool is_empty(const std::string_view &path, errco_t *err /*= nullptr*/) noexcept {
std::error_code ec;
bool ok = fs::is_empty(path, ec);
if (err != nullptr) {
*err = ec;
}
return ok;
}
errco_t check(const std::string_view &path) noexcept {
std::error_code ec;
bool ok = fs::exists(path, ec);
return ec;
}
errco_t setwd(const std::string_view &path) noexcept {
std::error_code ec;
fs::current_path(path, ec);
return ec;
}
std::string getwd() noexcept {
return fs::current_path().string();
}
std::string temp_dir(const std::string_view &more) noexcept {
std::error_code ec;
auto dir = fs::temp_directory_path(ec);
if (ec.value() == 0) {
return path::join(dir.string(), more);
} else {
return std::string(more);
}
}
std::string home_dir(const std::string_view &more) noexcept {
std::string result;
#ifdef WIN32
wchar_t path[MAX_PATH] = {0};
if (SUCCEEDED(::SHGetFolderPathW(nullptr, CSIDL_APPDATA, nullptr, 0, path))) {
result = strings::u16s2u8s(path);
}
#else
struct passwd *pw = ::getpwuid(::getuid());
if (pw != nullptr) {
result = pw->pw_dir;
}
#endif // WIN32
return path::join(result, more);
}
errco_t mkdir(const std::string_view &path) noexcept {
if (is_exists(path)) {
return no_error;
}
std::error_code ec;
fs::create_directory(path, ec);
return ec;
}
errco_t mkdir_all(const std::string_view &path) noexcept {
std::error_code ec;
fs::create_directories(path, ec);
return ec;
}
errco_t remove(const std::string_view &path) noexcept {
std::error_code ec;
fs::remove(path, ec);
return ec;
}
errco_t remove_all(const std::string_view &path) noexcept {
std::error_code ec;
fs::remove_all(path, ec);
return ec;
}
errco_t rename(const std::string_view &old_name, const std::string_view &new_name) noexcept {
std::error_code ec;
fs::rename(old_name, new_name, ec);
return ec;
}
errco_t copy_file(const std::string_view &from, const std::string_view &to) noexcept {
std::error_code ec;
fs::copy_file(from, to, fs::copy_options::overwrite_existing, ec);
return ec;
}
errco_t update_file(const std::string_view &from, const std::string_view &to) noexcept {
std::error_code ec;
fs::copy_file(from, to, fs::copy_options::update_existing, ec);
return ec;
}
errco_t copy_dir(const std::string_view &from, const std::string_view &to) noexcept {
std::error_code ec;
fs::copy(from, to, fs::copy_options::overwrite_existing | fs::copy_options::recursive, ec);
return ec;
}
errco_t update_dir(const std::string_view &from, const std::string_view &to) noexcept {
std::error_code ec;
fs::copy(from, to, fs::copy_options::update_existing | fs::copy_options::recursive, ec);
return ec;
}
//////////////////////////////////////////////////////////////////////////
errco_t file_size(const std::string_view &path, uint64_t *size) noexcept {
std::error_code ec;
*size = fs::file_size(path, ec);
return ec;
}
//////////////////////////////////////////////////////////////////////////
errco_t file_time(const std::string &path, FileTime *time) noexcept {
#ifdef WIN32
HANDLE handle = ::CreateFileW(
strings::u8s2u16s(path).c_str(), GENERIC_READ, FILE_SHARE_READ, nullptr, OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL | FILE_FLAG_BACKUP_SEMANTICS, nullptr);
if (handle == INVALID_HANDLE_VALUE) {
return last_error();
}
defer(::CloseHandle(handle));
uint64_t ctime = {0};
uint64_t atime = {0};
uint64_t mtime = {0};
if (!::GetFileTime(handle, reinterpret_cast<PFILETIME>(&ctime), reinterpret_cast<PFILETIME>(&atime), reinterpret_cast<PFILETIME>(&mtime))) {
return last_error();
}
time->atime = win32::filetime2nanotime(atime);
time->mtime = win32::filetime2nanotime(mtime);
time->ctime = win32::filetime2nanotime(ctime);
return no_error;
#else
struct stat st;
if (::stat(path.c_str(), &st) != 0) {
return last_error();
}
time->ctime = st.st_ctim.tv_sec * nanotime::nsec_of_one_sec + st.st_ctim.tv_nsec;
time->atime = st.st_atim.tv_sec * nanotime::nsec_of_one_sec + st.st_atim.tv_nsec;
time->mtime = st.st_mtim.tv_sec * nanotime::nsec_of_one_sec + st.st_mtim.tv_nsec;
return no_error;
#endif // WIN32
}
errco_t set_file_time(const std::string &path, const FileTime &time) noexcept {
#ifdef WIN32
HANDLE handle = ::CreateFileW(
strings::u8s2u16s(path).c_str(), GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, nullptr,
OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_BACKUP_SEMANTICS | FILE_WRITE_ATTRIBUTES, nullptr);
if (handle == INVALID_HANDLE_VALUE) {
return last_error();
}
defer(::CloseHandle(handle));
uint64_t ctime = win32::nanotime2filetime(time.ctime);
uint64_t atime = win32::nanotime2filetime(time.atime);
uint64_t mtime = win32::nanotime2filetime(time.mtime);
if (!::SetFileTime(handle, reinterpret_cast<PFILETIME>(&ctime), reinterpret_cast<PFILETIME>(&atime), reinterpret_cast<PFILETIME>(&mtime))) {
return last_error();
}
return no_error;
#else
struct timeval tv[2] = {0};
tv[0].tv_sec = nanotime::to_time_t(time.atime);
tv[0].tv_usec = (time.atime % nanotime::nsec_of_one_sec) / nanotime::nsec_of_one_usec;
tv[1].tv_sec = nanotime::to_time_t(time.mtime);
tv[1].tv_usec = (time.mtime % nanotime::nsec_of_one_sec) / nanotime::nsec_of_one_usec;
if (int code = ::utimes(path.c_str(), tv); code != 0) {
return last_error();
}
return no_error;
#endif // WIN32
}
//////////////////////////////////////////////////////////////////////////
errco_t file_mode(const std::string &path, uint32_t *mode) noexcept {
#ifdef WIN32
auto val = ::GetFileAttributesW(strings::u8s2u16s(path).c_str());
if (val == INVALID_FILE_ATTRIBUTES) {
return last_error();
}
*mode = 0;
if (val & FILE_ATTRIBUTE_READONLY) {
*mode |= FileMode::READONLY;
}
if (val & FILE_ATTRIBUTE_HIDDEN) {
*mode |= FileMode::HIDDEN;
}
if (val & FILE_ATTRIBUTE_DIRECTORY) {
*mode |= FileMode::DIRECTORY;
}
#else
struct stat st;
if (::stat(path.c_str(), &st) != 0) {
return last_error();
}
*mode = st.st_mode;
#endif // WIN32
return no_error;
}
errco_t set_file_mode(const std::string &path, uint32_t mode) noexcept {
#ifdef WIN32
DWORD val = 0;
if (mode & FileMode::READONLY) {
val |= FILE_ATTRIBUTE_READONLY;
}
if (mode & FileMode::HIDDEN) {
val |= FILE_ATTRIBUTE_HIDDEN;
}
if (!::SetFileAttributesW(strings::u8s2u16s(path).c_str(), val)) {
return last_error();
}
#else
if (::chmod(path.c_str(), mode) != 0) {
return last_error();
}
#endif // WIN32
return no_error;
}
errco_t chmod(const std::string &path, uint32_t mode) noexcept {
return set_file_mode(path, mode);
}
//////////////////////////////////////////////////////////////////////////
errco_t file_info(const std::string &path, FileInfo *info) noexcept {
#ifdef WIN32
WIN32_FILE_ATTRIBUTE_DATA fad = {0};
if (!::GetFileAttributesExW(strings::u8s2u16s(path).c_str(), GetFileExInfoStandard, &fad)) {
DWORD ec = ::GetLastError();
if (ec == ERROR_FILE_NOT_FOUND || ec == ERROR_PATH_NOT_FOUND) {
info->is_exists = false;
return no_error;
}
return errno_cast(ec);
}
info->is_exists = true;
info->is_dir = (fad.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY);
info->size = make_qword(fad.nFileSizeLow, fad.nFileSizeHigh);
auto &ft = info->times;
ft.atime = win32::filetime2nanotime(*reinterpret_cast<uint64_t *>(&fad.ftLastAccessTime));
ft.mtime = win32::filetime2nanotime(*reinterpret_cast<uint64_t *>(&fad.ftLastWriteTime));
ft.ctime = win32::filetime2nanotime(*reinterpret_cast<uint64_t *>(&fad.ftCreationTime));
auto &attr = fad.dwFileAttributes;
auto &modes = info->modes;
if (attr & FILE_ATTRIBUTE_READONLY) {
modes |= FileMode::READONLY;
}
if (attr & FILE_ATTRIBUTE_HIDDEN) {
modes |= FileMode::HIDDEN;
}
if (attr & FILE_ATTRIBUTE_DIRECTORY) {
modes |= FileMode::DIRECTORY;
}
#else
if (::access(path.c_str(), F_OK) != 0) {
if (errno == ENOENT) {
info->is_exists = false;
return no_error;
}
return last_error();
}
info->is_exists = true;
struct stat st;
if (::stat(path.c_str(), &st) != 0) {
return last_error();
}
info->is_dir = S_ISDIR(st.st_mode);
info->size = st.st_size;
auto &ft = info->times;
ft.ctime = st.st_ctim.tv_sec * nanotime::nsec_of_one_sec + st.st_ctim.tv_nsec;
ft.atime = st.st_atim.tv_sec * nanotime::nsec_of_one_sec + st.st_atim.tv_nsec;
ft.mtime = st.st_mtim.tv_sec * nanotime::nsec_of_one_sec + st.st_mtim.tv_nsec;
info->modes = st.st_mode;
#endif // WIN32
return no_error;
}
//////////////////////////////////////////////////////////////////////////
errco_t enum_dir(const std::string_view &dir, const std::function<bool(const std::string &, bool, uint64_t)> &func) noexcept {
try {
for (const auto &entry : fs::directory_iterator(dir)) {
if (!func(entry.path().string(), entry.is_directory(), entry.is_directory() ? 0 : entry.file_size())) {
break;
}
}
} catch (const std::exception &e) {
return e;
}
return no_error;
}
errco_t recursive_dir(const std::string_view &dir, const std::function<bool(const std::string &, bool, uint64_t)> &func) noexcept {
try {
for (const auto &entry : fs::recursive_directory_iterator(dir)) {
if (!func(entry.path().string(), entry.is_directory(), entry.is_directory() ? 0 : entry.file_size())) {
break;
}
}
} catch (const std::exception &e) {
return e;
}
return no_error;
}
//////////////////////////////////////////////////////////////////////////
errco_t read_file(const std::string &path, std::string *data) noexcept {
std::ifstream ifs(path, std::ios::binary | std::ios::ate);
if (!ifs.is_open()) {
return eprintf("os::read_file open error: %s", path.c_str());
}
std::streampos size = ifs.tellg();
if (size > 0) {
data->resize(size, 0);
ifs.seekg(0, std::ios::beg);
ifs.read(data->data(), size);
if (ifs.gcount() != size) {
return scl::eprintf("os::read_file read error: %s", path.c_str());
}
} else {
data->clear();
}
return no_error;
}
errco_t write_file(const std::string &path, const std::string &data, bool trunc /*= true*/) noexcept {
std::ofstream ofs(path, std::ios::binary | (trunc ? std::ios::trunc : std::ios::app));
if (!ofs.is_open()) {
return eprintf("os::write_file open error: %s", path.c_str());
}
if (!data.empty()) {
ofs.write(data.data(), data.size());
if (ofs.bad() || ofs.fail()) {
return eprintf("os::write_file write error: %s", path.c_str());
}
}
return no_error;
}
errco_t read_txt_file(const std::string &path, std::string *data, std::string *encoding) noexcept {
std::ifstream ifs(path);
if (!ifs.is_open()) {
return eprintf("os::read_txt_file open error: %s", path.c_str());
}
std::string line;
size_t num = 0;
data->clear();
while (std::getline(ifs, line)) {
if (num == 0) {
if (line.substr(0, 3) == "\xEF\xBB\xBF") {
if (encoding != nullptr) {
*encoding = "u8";
}
line = line.substr(3);
} else if (line.substr(0, 2) == "\xFF\xFE") {
if (encoding != nullptr) {
*encoding = "u16l";
}
line = line.substr(2);
} else if (line.substr(0, 2) == "\xFE\xFF") {
if (encoding != nullptr) {
*encoding = "u16b";
}
line = line.substr(2);
} else {
if (encoding != nullptr) {
*encoding = "u8";
}
}
}
data->append(line);
data->append("\n");
++num;
}
if (ifs.eof()) {
return no_error;
} else {
return eprintf("os::read_txt_file read error: %s", path.c_str());
}
}
errco_t read_txt_file(const std::string &path, std::vector<std::string> *data, std::string *encoding) noexcept {
std::ifstream ifs(path);
if (!ifs.is_open()) {
return eprintf("os::read_txt_file open error: %s", path.c_str());
}
std::string line;
size_t num = 0;
data->clear();
while (std::getline(ifs, line)) {
if (num == 0) {
if (line.substr(0, 3) == "\xEF\xBB\xBF") {
if (encoding != nullptr) {
*encoding = "u8";
}
line = line.substr(3);
} else if (line.substr(0, 2) == "\xFF\xFE") {
if (encoding != nullptr) {
*encoding = "u16l";
}
line = line.substr(2);
} else if (line.substr(0, 2) == "\xFE\xFF") {
if (encoding != nullptr) {
*encoding = "u16b";
}
line = line.substr(2);
} else {
if (encoding != nullptr) {
*encoding = "u8";
}
}
}
data->push_back(line);
++num;
}
if (ifs.eof()) {
return no_error;
} else {
return eprintf("os::read_txt_file read error: %s", path.c_str());
}
}
errco_t write_txt_file(const std::string &path, const std::string &data, const std::string &encoding /*= "u8"*/) noexcept {
std::ofstream ofs(path, std::ios::binary | std::ios::trunc);
if (!ofs.is_open()) {
return eprintf("os::write_txt_file open error: %s", path.c_str());
}
std::string bom;
if (encoding == "u8b") {
bom = "\xEF\xBB\xBF";
} else if (encoding == "u16lb") {
bom = "\xFF\xFE";
} else if (encoding == "u16lb") {
bom = "\xFE\xFF";
}
if (!bom.empty()) {
ofs.write(bom.data(), bom.size());
if (ofs.bad() || ofs.fail()) {
return eprintf("os::write_txt_file write bom error: %s", path.c_str());
}
}
ofs.write(data.data(), data.size());
if (ofs.bad() || ofs.fail()) {
return eprintf("os::write_txt_file write data error: %s", path.c_str());
}
return no_error;
}
errco_t create_null_file(const std::string &path, uint64_t mb_bytes) {
#ifdef WIN32
auto cmd = strings::printf("fsutil file createnew %s %lld >nul 2>nul", path.c_str(), mb_bytes * 1024 * 1024);
#else
auto cmd = strings::printf("dd if=/dev/zero of=%s bs=1M count=%lld > /dev/null 2>&1", path.c_str(), mb_bytes);
#endif // WIN32
if (int code = std::system(cmd.c_str()); code != 0) {
return eprintf(code, "create failed: %s", path.c_str());
}
return no_error;
}
//////////////////////////////////////////////////////////////////////////
} // namespace scl::os
//////////////////////////////////////////////////////////////////////////