C++ OS相关。

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

//////////////////////////////////////////////////////////////////////////
相关推荐
仰泳的熊猫5 小时前
1176 The Closest Fibonacci Number
数据结构·c++·算法·pat考试
点云SLAM5 小时前
C++中constexpr 与 explicit关键字使用详解
c++·explicit关键字·隐式转换·c++编译·constexpr关键字·c++11/17/20
宠..5 小时前
获取输入内容
开发语言·c++·qt
郝学胜-神的一滴5 小时前
Linux系统调用中断机制深度解析
linux·运维·服务器·开发语言·c++·程序人生
chenyuhao20246 小时前
Linux系统编程:Ext文件系统
linux·运维·服务器·开发语言·网络·c++·后端
hd51cc6 小时前
MFC运行原理
c++·mfc
小此方6 小时前
Re:从零开始学C++(二)基础精讲·中篇:引用
开发语言·c++·底层
天赐学c语言7 小时前
12.13 - 岛屿数量 && C语言中extern关键字的作用
c++·算法·leetcode
郭涤生7 小时前
大白话Proactor模式
linux·网络·c++