summaryrefslogtreecommitdiffhomepage
path: root/tooling
diff options
context:
space:
mode:
authorAmlal El Mahrouss <amlal@nekernel.org>2025-05-02 19:38:46 +0200
committerGitHub <noreply@github.com>2025-05-02 19:38:46 +0200
commit997be16e5ac9a68d54882ab69529815860d62955 (patch)
tree19d6129c2d776bb1edc5d4a7325e39ca176c3403 /tooling
parent618104e74c195d7508a18450524f8ed7f9af8cc6 (diff)
parentb3b4b1ebdcd6adeac914869017c86d892b7a8ced (diff)
Merge pull request #28 from nekernel-org/dev
0.0.2
Diffstat (limited to 'tooling')
-rw-r--r--tooling/dist/.keep0
-rw-r--r--tooling/fsck.hefs.cc15
-rw-r--r--tooling/fsck.hefs.json16
-rw-r--r--tooling/hefs.h109
-rwxr-xr-xtooling/mk_fwrk.py71
-rwxr-xr-xtooling/mk_img.py49
-rw-r--r--tooling/mkfs.h36
-rw-r--r--tooling/mkfs.hefs.cc105
-rw-r--r--tooling/mkfs.hefs.json16
-rw-r--r--tooling/rang.h467
10 files changed, 884 insertions, 0 deletions
diff --git a/tooling/dist/.keep b/tooling/dist/.keep
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/tooling/dist/.keep
diff --git a/tooling/fsck.hefs.cc b/tooling/fsck.hefs.cc
new file mode 100644
index 00000000..05cf0660
--- /dev/null
+++ b/tooling/fsck.hefs.cc
@@ -0,0 +1,15 @@
+/* -------------------------------------------
+
+ Copyright (C) 2025, Amlal El Mahrouss, all rights reserved.
+
+------------------------------------------- */
+
+#include <tooling/hefs.h>
+#include <cstdlib>
+
+int main(int argc, char** argv) {
+ (void) (argc);
+ (void) (argv);
+
+ return EXIT_FAILURE;
+} \ No newline at end of file
diff --git a/tooling/fsck.hefs.json b/tooling/fsck.hefs.json
new file mode 100644
index 00000000..d6ff4b0f
--- /dev/null
+++ b/tooling/fsck.hefs.json
@@ -0,0 +1,16 @@
+{
+ "compiler_path": "g++",
+ "compiler_std": "c++20",
+ "headers_path": [
+ "../"
+ ],
+ "sources_path": [
+ "fsck.hefs.cc"
+ ],
+ "output_name": "./dist/fsck.hefs",
+ "cpp_macros": [
+ "kFSCKHEFSVersion=0x0100",
+ "kFSCKHEFSVersionHighest=0x0100",
+ "kFSCKHEFSVersionLowest=0x0100"
+ ]
+} \ No newline at end of file
diff --git a/tooling/hefs.h b/tooling/hefs.h
new file mode 100644
index 00000000..99d04768
--- /dev/null
+++ b/tooling/hefs.h
@@ -0,0 +1,109 @@
+/* -------------------------------------------
+
+ Copyright (C) 2024-2025, Amlal El Mahrouss, all rights reserved.
+
+------------------------------------------- */
+
+#pragma once
+
+#include <cstdint>
+#include <cstring>
+
+#define kHeFSVersion (0x0101)
+#define kHeFSMagic " HeFS"
+#define kHeFSMagicLen (8)
+
+#define kHeFSFileNameLen (256U)
+#define kHeFSPartNameLen (128U)
+
+#define kHeFSDefaultVoluneName u8"HeFS Volume"
+
+namespace mkfs::hefs {
+
+// Drive kinds
+enum {
+ kHeFSHardDrive = 0xC0, // Hard Drive
+ kHeFSSolidStateDrive = 0xC1, // Solid State Drive
+ kHeFSOpticalDrive = 0x0C, // Blu-Ray/DVD
+ kHeFSMassStorageDevice = 0xCC, // USB
+ kHeFSScsiDrive = 0xC4, // SCSI Hard Drive
+ kHeFSFlashDrive = 0xC6,
+ kHeFSUnknown = 0xFF, // Unknown device.
+ kHeFSDriveCount = 7,
+};
+
+// Disk status
+enum {
+ kHeFSStatusUnlocked = 0x18,
+ kHeFSStatusLocked,
+ kHeFSStatusError,
+ kHeFSStatusInvalid,
+ kHeFSStatusCount,
+};
+
+// Encodings
+enum {
+ kHeFSEncodingUTF8 = 0x00,
+ kHeFSEncodingUTF16,
+ kHeFSEncodingUTF32,
+ kHeFSEncodingUTF16BE,
+ kHeFSEncodingUTF16LE,
+ kHeFSEncodingUTF32BE,
+ kHeFSEncodingUTF32LE,
+ kHeFSEncodingUTF8BE,
+ kHeFSEncodingUTF8LE,
+ kHeFSEncodingBinary,
+ kHeFSEncodingCount,
+};
+
+// Time type
+using ATime = std::uint64_t;
+
+// File kinds
+inline constexpr uint16_t kHeFSFileKindRegular = 0x00;
+inline constexpr uint16_t kHeFSFileKindDirectory = 0x01;
+inline constexpr uint16_t kHeFSFileKindBlock = 0x02;
+inline constexpr uint16_t kHeFSFileKindCharacter = 0x03;
+inline constexpr uint16_t kHeFSFileKindFIFO = 0x04;
+inline constexpr uint16_t kHeFSFileKindSocket = 0x05;
+inline constexpr uint16_t kHeFSFileKindSymbolicLink = 0x06;
+inline constexpr uint16_t kHeFSFileKindUnknown = 0x07;
+inline constexpr uint16_t kHeFSFileKindCount = 0x08;
+
+// Red-black tree colors
+enum {
+ kHeFSInvalidColor = 0,
+ kHeFSRed = 100,
+ kHeFSBlack,
+ kHeFSColorCount,
+};
+
+// Time constants
+inline constexpr ATime kHeFSTimeInvalid = 0x0000000000000000;
+inline constexpr ATime kHeFSTimeMax = 0xFFFFFFFFFFFFFFFF - 1;
+
+// Boot Node
+struct __attribute__((packed)) BootNode {
+ char magic[kHeFSMagicLen]{};
+ char8_t volumeName[kHeFSPartNameLen]{};
+ std::uint32_t version{};
+ std::uint64_t badSectors{};
+ std::uint64_t sectorCount{};
+ std::uint64_t sectorSize{};
+ std::uint32_t checksum{};
+ std::uint8_t diskKind{};
+ std::uint8_t encoding{};
+ std::uint64_t startIND{};
+ std::uint64_t endIND{};
+ std::uint64_t indCount{};
+ std::uint64_t diskSize{};
+ std::uint16_t diskStatus{};
+ std::uint16_t diskFlags{};
+ std::uint16_t vid{};
+ std::uint64_t startIN{};
+ std::uint64_t endIN{};
+ std::uint64_t reserved3{};
+ std::uint64_t reserved4{};
+ char pad[272]{};
+};
+} // namespace mkfs::hefs
diff --git a/tooling/mk_fwrk.py b/tooling/mk_fwrk.py
new file mode 100755
index 00000000..e0302754
--- /dev/null
+++ b/tooling/mk_fwrk.py
@@ -0,0 +1,71 @@
+#! /usr/bin/env python3
+# -*- coding: utf-8 -*-
+
+import os
+import json
+import sys
+
+def create_directory_structure(base_path, project_name):
+ # Define the directory structure
+ structure = {
+ project_name: {
+ "dist": {
+ ".keep": None
+ },
+ "src": {
+ ".keep": None
+ },
+ "vendor": {
+ ".keep": None
+ },
+ ".keep": None,
+ f"{project_name}.json": {}
+ }
+ }
+
+ def create_structure(path, structure):
+ for name, content in structure.items():
+ current_path = os.path.join(path, name)
+ # Create directories or files based on the content type
+ if isinstance(content, dict) and current_path.endswith(".json") == False:
+ os.makedirs(current_path, exist_ok=True)
+ create_structure(current_path, content)
+ elif content is None:
+ # Create an empty file
+ with open(current_path, 'w') as f:
+ pass
+
+ # Create the base directory
+ os.makedirs(base_path, exist_ok=True)
+ create_structure(base_path, structure)
+
+ # Create the JSON file
+ diutil_json_path = os.path.join(base_path, project_name, f"{project_name}.json")
+ manifest = {
+ "compiler_path": "clang++",
+ "compiler_std": "c++20",
+ "headers_path": ["./", "../../../dev/kernel", "../../../public/frameworks/", "../../../dev/", "./"],
+ "sources_path": [
+
+ ],
+ "output_name": f"./dist/{project_name}",
+ "cpp_macros": [
+ "kSampleFWVersion=0x0100",
+ "kSampleFWVersionHighest=0x0100",
+ "kSampleFWVersionLowest=0x0100",
+ "__NE_SDK__"
+ ]
+ }
+
+ with open(diutil_json_path, 'w') as json_file:
+ json.dump(manifest, json_file, indent=4)
+
+if __name__ == "__main__":
+ if len(sys.argv) != 2:
+ print("Usage: mk_fwrk.py <project_name>")
+ sys.exit(os.EX_CONFIG)
+
+ base_path = os.getcwd() # Use the current working directory as the base path
+ create_directory_structure(base_path, sys.argv[1])
+
+ print("NeKernel framework created successfully.")
diff --git a/tooling/mk_img.py b/tooling/mk_img.py
new file mode 100755
index 00000000..8227a217
--- /dev/null
+++ b/tooling/mk_img.py
@@ -0,0 +1,49 @@
+#!/usr/bin/env python3
+# -*- coding: utf-8 -*-
+
+import os
+import sys
+import subprocess
+import glob
+
+def copy_to_fat(image_path, source_dir):
+ if not os.path.isfile(image_path):
+ print(f"Error: FAT32 image {image_path} does not exist.")
+ sys.exit(1)
+
+ if not os.path.isdir(source_dir):
+ print(f"Error: {source_dir} is not a valid directory.")
+ sys.exit(1)
+
+ try:
+ files_to_copy = glob.glob(os.path.join(source_dir, "*"))
+
+ if not files_to_copy:
+ print(f"Warning: No files found in {source_dir}. Nothing to copy.")
+ return
+
+ command = ["mcopy", "-spm", "-i", image_path] + files_to_copy + ["::"]
+ subprocess.run(command, check=True)
+
+ print(f"Info: Successfully copied contents of '{source_dir}' into '{image_path}'")
+ except FileNotFoundError:
+ print("Error: mcopy is not installed. Please install mtools.")
+ sys.exit(1)
+ except subprocess.CalledProcessError as e:
+ print(f"Error: mcopy failed with error code {e.returncode}.")
+ sys.exit(1)
+ except Exception as e:
+ print(f"Error: failed: {e}")
+ sys.exit(1)
+
+if __name__ == "__main__":
+ if len(sys.argv) != 3:
+ print("Usage: mk_img.py <fat32_image> <source_directory>")
+ sys.exit(1)
+
+ image_path = sys.argv[1]
+ source_dir = sys.argv[2]
+
+ copy_to_fat(image_path, source_dir)
+
+ print("NeKernel image created successfully.")
diff --git a/tooling/mkfs.h b/tooling/mkfs.h
new file mode 100644
index 00000000..7180b179
--- /dev/null
+++ b/tooling/mkfs.h
@@ -0,0 +1,36 @@
+/* -------------------------------------------
+
+ Copyright (C) 2025, Amlal El Mahrouss, all rights reserved.
+
+------------------------------------------- */
+
+#pragma once
+
+#include <tooling/rang.h>
+#include <iostream>
+#include <string>
+
+/// @internal
+namespace mkfs {
+/// @brief Helper function to get the option value from command line arguments.
+template <typename CharType>
+inline std::basic_string<CharType> get_option(const std::basic_string<CharType>& args,
+ const std::basic_string<CharType>& option) {
+ size_t pos = args.find(option + CharType('='));
+
+ if (pos != std::string::npos) {
+ size_t start = pos + option.length() + 1;
+ size_t end = args.find(' ', start);
+ return args.substr(start, end - start);
+ }
+
+ return std::basic_string<CharType>{};
+}
+
+inline auto console_out() -> std::ostream& {
+ std::ostream& conout = std::cout;
+ conout << rang::fg::red << "mkfs: " << rang::style::reset;
+
+ return conout;
+}
+} // namespace mkfs \ No newline at end of file
diff --git a/tooling/mkfs.hefs.cc b/tooling/mkfs.hefs.cc
new file mode 100644
index 00000000..23772023
--- /dev/null
+++ b/tooling/mkfs.hefs.cc
@@ -0,0 +1,105 @@
+/* -------------------------------------------
+
+ Copyright (C) 2025, Amlal El Mahrouss, all rights reserved.
+
+------------------------------------------- */
+
+#include <tooling/hefs.h>
+#include <tooling/mkfs.h>
+#include <cstdlib>
+#include <fstream>
+
+static size_t kDiskSize = 1024 * 1024 * 1024 * 4UL;
+static uint16_t kVersion = kHeFSVersion;
+static std::u8string kLabel = kHeFSDefaultVoluneName;
+static size_t kSectorSize = 512;
+
+int main(int argc, char** argv) {
+ if (argc < 2) {
+ mkfs::console_out() << "hefs: Usage: mkfs.hefs -L <label> -s <sector_size> -p <part_start> -e "
+ << "<part_end> -S <disk_size> -o <output_device>" << std::endl;
+
+ return EXIT_FAILURE;
+ }
+
+ std::string args;
+ std::u8string args_wide;
+
+ for (int i = 1; i < argc; ++i) {
+ args += argv[i];
+ args += " ";
+
+ std::string str = argv[i];
+
+ for (auto& ch : str) {
+ args_wide.push_back(ch);
+ }
+
+ args_wide += u8" ";
+ }
+
+ auto output_device = mkfs::get_option<char>(args, "-o");
+
+ kSectorSize = std::strtol(mkfs::get_option<char>(args, "-s").data(), nullptr, 10);
+ kLabel = mkfs::get_option<char8_t>(args_wide, u8"-L");
+
+ if (kLabel.empty()) kLabel = kHeFSDefaultVoluneName;
+
+ kDiskSize =
+ std::strtol(mkfs::get_option<char>(args, "-S").data(), nullptr, 10) * 1024 * 1024 * 1024;
+
+ if (kDiskSize == 0) {
+ mkfs::console_out() << "hefs: Error: Unable to deduce future disk size for output_device: "
+ << output_device << std::endl;
+ return EXIT_FAILURE;
+ }
+
+ // Open the output_device
+ std::ofstream filesystem(output_device, std::ios::binary);
+
+ if (!filesystem.good()) {
+ mkfs::console_out() << "hefs: Info: Unable to open output_device: " << output_device
+ << std::endl;
+ return EXIT_FAILURE;
+ }
+
+ // create a boot node, and then allocate a index node directory tree.
+ mkfs::hefs::BootNode bootNode{{}, {}, 0, 0, 0, 0, 0, 0, 0, 0};
+
+ auto start_ind = std::strtol(mkfs::get_option<char>(args, "-p").data(), nullptr, 10);
+
+ start_ind += sizeof(mkfs::hefs::BootNode);
+
+ auto end_ind = std::strtol(mkfs::get_option<char>(args, "-e").data(), nullptr, 10);
+
+ bootNode.version = kVersion;
+ bootNode.diskKind = mkfs::hefs::kHeFSHardDrive;
+ bootNode.encoding = mkfs::hefs::kHeFSEncodingUTF8;
+ bootNode.diskSize = kDiskSize;
+ bootNode.sectorSize = kSectorSize;
+ bootNode.startIND = start_ind;
+ bootNode.endIND = end_ind;
+ bootNode.indCount = 0UL;
+ bootNode.diskStatus = mkfs::hefs::kHeFSStatusUnlocked;
+
+ std::memcpy(bootNode.magic, kHeFSMagic, kHeFSMagicLen - 1);
+ std::memcpy(bootNode.volumeName, kLabel.data(), kLabel.size() * sizeof(char16_t));
+
+ filesystem.seekp(std::strtol(mkfs::get_option<char>(args, "-p").data(), nullptr, 10));
+ filesystem.write(reinterpret_cast<const char*>(&bootNode), sizeof(mkfs::hefs::BootNode));
+
+ if (!filesystem.good()) {
+ mkfs::console_out() << "hefs: Error: Unable to write FS to output_device: " << output_device
+ << std::endl;
+ return EXIT_FAILURE;
+ }
+
+ filesystem.seekp(bootNode.startIND);
+
+ filesystem.flush();
+ filesystem.close();
+
+ mkfs::console_out() << "hefs: Info: Wrote FS to output_device: " << output_device << std::endl;
+
+ return EXIT_SUCCESS;
+} \ No newline at end of file
diff --git a/tooling/mkfs.hefs.json b/tooling/mkfs.hefs.json
new file mode 100644
index 00000000..d29b7f73
--- /dev/null
+++ b/tooling/mkfs.hefs.json
@@ -0,0 +1,16 @@
+{
+ "compiler_path": "g++",
+ "compiler_std": "c++20",
+ "headers_path": [
+ "../"
+ ],
+ "sources_path": [
+ "mkfs.hefs.cc"
+ ],
+ "output_name": "./dist/mkfs.hefs",
+ "cpp_macros": [
+ "kMKFSHEFSVersion=0x0100",
+ "kMKFSHEFSVersionHighest=0x0100",
+ "kMKFSHEFSVersionLowest=0x0100"
+ ]
+} \ No newline at end of file
diff --git a/tooling/rang.h b/tooling/rang.h
new file mode 100644
index 00000000..5d1c68ef
--- /dev/null
+++ b/tooling/rang.h
@@ -0,0 +1,467 @@
+#ifndef RANG_DOT_HPP
+#define RANG_DOT_HPP
+
+#if defined(__unix__) || defined(__unix) || defined(__linux__)
+#define OS_LINUX
+#elif defined(WIN32) || defined(_WIN32) || defined(_WIN64)
+#define OS_WIN
+#elif defined(__APPLE__) || defined(__MACH__)
+#define OS_MAC
+#else
+#error Unknown Platform
+#endif
+
+#if defined(OS_LINUX) || defined(OS_MAC)
+#include <unistd.h>
+
+#elif defined(OS_WIN)
+
+#if defined(_WIN32_WINNT) && (_WIN32_WINNT < 0x0600)
+#error \
+ "Please include rang.hpp before any windows system headers or set _WIN32_WINNT at least to _WIN32_WINNT_VISTA"
+#elif !defined(_WIN32_WINNT)
+#define _WIN32_WINNT _WIN32_WINNT_VISTA
+#endif
+
+#include <io.h>
+#include <windows.h>
+#include <memory>
+
+// Only defined in windows 10 onwards, redefining in lower windows since it
+// doesn't gets used in lower versions
+// https://docs.microsoft.com/en-us/windows/console/getconsolemode
+#ifndef ENABLE_VIRTUAL_TERMINAL_PROCESSING
+#define ENABLE_VIRTUAL_TERMINAL_PROCESSING 0x0004
+#endif
+
+#endif
+
+#include <algorithm>
+#include <atomic>
+#include <cstdlib>
+#include <cstring>
+#include <iostream>
+
+namespace rang {
+
+/* For better compability with most of terminals do not use any style settings
+ * except of reset, bold and reversed.
+ * Note that on Windows terminals bold style is same as fgB color.
+ */
+enum class style {
+ reset = 0,
+ bold = 1,
+ dim = 2,
+ italic = 3,
+ underline = 4,
+ blink = 5,
+ rblink = 6,
+ reversed = 7,
+ conceal = 8,
+ crossed = 9
+};
+
+enum class fg {
+ black = 30,
+ red = 31,
+ green = 32,
+ yellow = 33,
+ blue = 34,
+ magenta = 35,
+ cyan = 36,
+ gray = 37,
+ reset = 39
+};
+
+enum class bg {
+ black = 40,
+ red = 41,
+ green = 42,
+ yellow = 43,
+ blue = 44,
+ magenta = 45,
+ cyan = 46,
+ gray = 47,
+ reset = 49
+};
+
+enum class fgB {
+ black = 90,
+ red = 91,
+ green = 92,
+ yellow = 93,
+ blue = 94,
+ magenta = 95,
+ cyan = 96,
+ gray = 97
+};
+
+enum class bgB {
+ black = 100,
+ red = 101,
+ green = 102,
+ yellow = 103,
+ blue = 104,
+ magenta = 105,
+ cyan = 106,
+ gray = 107
+};
+
+enum class control { // Behaviour of rang function calls
+ Off = 0, // toggle off rang style/color calls
+ Auto = 1, // (Default) autodect terminal and colorize if needed
+ Force = 2 // force ansi color output to non terminal streams
+};
+// Use rang::setControlMode to set rang control mode
+
+enum class winTerm { // Windows Terminal Mode
+ Auto = 0, // (Default) automatically detects wheter Ansi or Native API
+ Ansi = 1, // Force use Ansi API
+ Native = 2 // Force use Native API
+};
+// Use rang::setWinTermMode to explicitly set terminal API for Windows
+// Calling rang::setWinTermMode have no effect on other OS
+
+namespace rang_implementation {
+
+ inline std::atomic<control>& controlMode() noexcept {
+ static std::atomic<control> value(control::Auto);
+ return value;
+ }
+
+ inline std::atomic<winTerm>& winTermMode() noexcept {
+ static std::atomic<winTerm> termMode(winTerm::Auto);
+ return termMode;
+ }
+
+ inline bool supportsColor() noexcept {
+#if defined(OS_LINUX) || defined(OS_MAC)
+
+ static const bool result = [] {
+ const char* Terms[] = {"ansi", "color", "console", "cygwin", "gnome", "konsole", "kterm",
+ "linux", "msys", "putty", "rxvt", "screen", "vt100", "xterm"};
+
+ const char* env_p = std::getenv("TERM");
+ if (env_p == nullptr) {
+ return false;
+ }
+ return std::any_of(std::begin(Terms), std::end(Terms),
+ [&](const char* term) { return std::strstr(env_p, term) != nullptr; });
+ }();
+
+#elif defined(OS_WIN)
+ // All windows versions support colors through native console methods
+ static constexpr bool result = true;
+#endif
+ return result;
+ }
+
+#ifdef OS_WIN
+
+ inline bool isMsysPty(int fd) noexcept {
+ // Dynamic load for binary compability with old Windows
+ const auto ptrGetFileInformationByHandleEx =
+ reinterpret_cast<decltype(&GetFileInformationByHandleEx)>(
+ GetProcAddress(GetModuleHandle(TEXT("kernel32.dll")), "GetFileInformationByHandleEx"));
+ if (!ptrGetFileInformationByHandleEx) {
+ return false;
+ }
+
+ HANDLE h = reinterpret_cast<HANDLE>(_get_osfhandle(fd));
+ if (h == INVALID_HANDLE_VALUE) {
+ return false;
+ }
+
+ // Check that it's a pipe:
+ if (GetFileType(h) != FILE_TYPE_PIPE) {
+ return false;
+ }
+
+ // POD type is binary compatible with FILE_NAME_INFO from WinBase.h
+ // It have the same alignment and used to avoid UB in caller code
+ struct MY_FILE_NAME_INFO {
+ DWORD FileNameLength;
+ WCHAR FileName[MAX_PATH];
+ };
+
+ auto pNameInfo = std::unique_ptr<MY_FILE_NAME_INFO>(new (std::nothrow) MY_FILE_NAME_INFO());
+ if (!pNameInfo) {
+ return false;
+ }
+
+ // Check pipe name is template of
+ // {"cygwin-","msys-"}XXXXXXXXXXXXXXX-ptyX-XX
+ if (!ptrGetFileInformationByHandleEx(h, FileNameInfo, pNameInfo.get(),
+ sizeof(MY_FILE_NAME_INFO))) {
+ return false;
+ }
+ std::wstring name(pNameInfo->FileName, pNameInfo->FileNameLength / sizeof(WCHAR));
+ if ((name.find(L"msys-") == std::wstring::npos &&
+ name.find(L"cygwin-") == std::wstring::npos) ||
+ name.find(L"-pty") == std::wstring::npos) {
+ return false;
+ }
+
+ return true;
+ }
+
+#endif
+
+ inline bool isTerminal(const std::streambuf* osbuf) noexcept {
+ using std::cerr;
+ using std::clog;
+ using std::cout;
+#if defined(OS_LINUX) || defined(OS_MAC)
+ if (osbuf == cout.rdbuf()) {
+ static const bool cout_term = isatty(fileno(stdout)) != 0;
+ return cout_term;
+ } else if (osbuf == cerr.rdbuf() || osbuf == clog.rdbuf()) {
+ static const bool cerr_term = isatty(fileno(stderr)) != 0;
+ return cerr_term;
+ }
+#elif defined(OS_WIN)
+ if (osbuf == cout.rdbuf()) {
+ static const bool cout_term = (_isatty(_fileno(stdout)) || isMsysPty(_fileno(stdout)));
+ return cout_term;
+ } else if (osbuf == cerr.rdbuf() || osbuf == clog.rdbuf()) {
+ static const bool cerr_term = (_isatty(_fileno(stderr)) || isMsysPty(_fileno(stderr)));
+ return cerr_term;
+ }
+#endif
+ return false;
+ }
+
+ template <typename T>
+ using enableStd = typename std::enable_if<
+ std::is_same<T, rang::style>::value || std::is_same<T, rang::fg>::value ||
+ std::is_same<T, rang::bg>::value || std::is_same<T, rang::fgB>::value ||
+ std::is_same<T, rang::bgB>::value,
+ std::ostream&>::type;
+
+#ifdef OS_WIN
+
+ struct SGR { // Select Graphic Rendition parameters for Windows console
+ BYTE fgColor; // foreground color (0-15) lower 3 rgb bits + intense bit
+ BYTE bgColor; // background color (0-15) lower 3 rgb bits + intense bit
+ BYTE bold; // emulated as FOREGROUND_INTENSITY bit
+ BYTE underline; // emulated as BACKGROUND_INTENSITY bit
+ BOOLEAN inverse; // swap foreground/bold & background/underline
+ BOOLEAN conceal; // set foreground/bold to background/underline
+ };
+
+ enum class AttrColor : BYTE { // Color attributes for console screen buffer
+ black = 0,
+ red = 4,
+ green = 2,
+ yellow = 6,
+ blue = 1,
+ magenta = 5,
+ cyan = 3,
+ gray = 7
+ };
+
+ inline HANDLE getConsoleHandle(const std::streambuf* osbuf) noexcept {
+ if (osbuf == std::cout.rdbuf()) {
+ static const HANDLE hStdout = GetStdHandle(STD_OUTPUT_HANDLE);
+ return hStdout;
+ } else if (osbuf == std::cerr.rdbuf() || osbuf == std::clog.rdbuf()) {
+ static const HANDLE hStderr = GetStdHandle(STD_ERROR_HANDLE);
+ return hStderr;
+ }
+ return INVALID_HANDLE_VALUE;
+ }
+
+ inline bool setWinTermAnsiColors(const std::streambuf* osbuf) noexcept {
+ HANDLE h = getConsoleHandle(osbuf);
+ if (h == INVALID_HANDLE_VALUE) {
+ return false;
+ }
+ DWORD dwMode = 0;
+ if (!GetConsoleMode(h, &dwMode)) {
+ return false;
+ }
+ dwMode |= ENABLE_VIRTUAL_TERMINAL_PROCESSING;
+ if (!SetConsoleMode(h, dwMode)) {
+ return false;
+ }
+ return true;
+ }
+
+ inline bool supportsAnsi(const std::streambuf* osbuf) noexcept {
+ using std::cerr;
+ using std::clog;
+ using std::cout;
+ if (osbuf == cout.rdbuf()) {
+ static const bool cout_ansi = (isMsysPty(_fileno(stdout)) || setWinTermAnsiColors(osbuf));
+ return cout_ansi;
+ } else if (osbuf == cerr.rdbuf() || osbuf == clog.rdbuf()) {
+ static const bool cerr_ansi = (isMsysPty(_fileno(stderr)) || setWinTermAnsiColors(osbuf));
+ return cerr_ansi;
+ }
+ return false;
+ }
+
+ inline const SGR& defaultState() noexcept {
+ static const SGR defaultSgr = []() -> SGR {
+ CONSOLE_SCREEN_BUFFER_INFO info;
+ WORD attrib = FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE;
+ if (GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), &info) ||
+ GetConsoleScreenBufferInfo(GetStdHandle(STD_ERROR_HANDLE), &info)) {
+ attrib = info.wAttributes;
+ }
+ SGR sgr = {0, 0, 0, 0, FALSE, FALSE};
+ sgr.fgColor = attrib & 0x0F;
+ sgr.bgColor = (attrib & 0xF0) >> 4;
+ return sgr;
+ }();
+ return defaultSgr;
+ }
+
+ inline BYTE ansi2attr(BYTE rgb) noexcept {
+ static const AttrColor rev[8] = {AttrColor::black, AttrColor::red, AttrColor::green,
+ AttrColor::yellow, AttrColor::blue, AttrColor::magenta,
+ AttrColor::cyan, AttrColor::gray};
+ return static_cast<BYTE>(rev[rgb]);
+ }
+
+ inline void setWinSGR(rang::bg col, SGR& state) noexcept {
+ if (col != rang::bg::reset) {
+ state.bgColor = ansi2attr(static_cast<BYTE>(col) - 40);
+ } else {
+ state.bgColor = defaultState().bgColor;
+ }
+ }
+
+ inline void setWinSGR(rang::fg col, SGR& state) noexcept {
+ if (col != rang::fg::reset) {
+ state.fgColor = ansi2attr(static_cast<BYTE>(col) - 30);
+ } else {
+ state.fgColor = defaultState().fgColor;
+ }
+ }
+
+ inline void setWinSGR(rang::bgB col, SGR& state) noexcept {
+ state.bgColor = (BACKGROUND_INTENSITY >> 4) | ansi2attr(static_cast<BYTE>(col) - 100);
+ }
+
+ inline void setWinSGR(rang::fgB col, SGR& state) noexcept {
+ state.fgColor = FOREGROUND_INTENSITY | ansi2attr(static_cast<BYTE>(col) - 90);
+ }
+
+ inline void setWinSGR(rang::style style, SGR& state) noexcept {
+ switch (style) {
+ case rang::style::reset:
+ state = defaultState();
+ break;
+ case rang::style::bold:
+ state.bold = FOREGROUND_INTENSITY;
+ break;
+ case rang::style::underline:
+ case rang::style::blink:
+ state.underline = BACKGROUND_INTENSITY;
+ break;
+ case rang::style::reversed:
+ state.inverse = TRUE;
+ break;
+ case rang::style::conceal:
+ state.conceal = TRUE;
+ break;
+ default:
+ break;
+ }
+ }
+
+ inline SGR& current_state() noexcept {
+ static SGR state = defaultState();
+ return state;
+ }
+
+ inline WORD SGR2Attr(const SGR& state) noexcept {
+ WORD attrib = 0;
+ if (state.conceal) {
+ if (state.inverse) {
+ attrib = (state.fgColor << 4) | state.fgColor;
+ if (state.bold) attrib |= FOREGROUND_INTENSITY | BACKGROUND_INTENSITY;
+ } else {
+ attrib = (state.bgColor << 4) | state.bgColor;
+ if (state.underline) attrib |= FOREGROUND_INTENSITY | BACKGROUND_INTENSITY;
+ }
+ } else if (state.inverse) {
+ attrib = (state.fgColor << 4) | state.bgColor;
+ if (state.bold) attrib |= BACKGROUND_INTENSITY;
+ if (state.underline) attrib |= FOREGROUND_INTENSITY;
+ } else {
+ attrib = state.fgColor | (state.bgColor << 4) | state.bold | state.underline;
+ }
+ return attrib;
+ }
+
+ template <typename T>
+ inline void setWinColorAnsi(std::ostream& os, T const value) {
+ os << "\033[" << static_cast<int>(value) << "m";
+ }
+
+ template <typename T>
+ inline void setWinColorNative(std::ostream& os, T const value) {
+ const HANDLE h = getConsoleHandle(os.rdbuf());
+ if (h != INVALID_HANDLE_VALUE) {
+ setWinSGR(value, current_state());
+ // Out all buffered text to console with previous settings:
+ os.flush();
+ SetConsoleTextAttribute(h, SGR2Attr(current_state()));
+ }
+ }
+
+ template <typename T>
+ inline enableStd<T> setColor(std::ostream& os, T const value) {
+ if (winTermMode() == winTerm::Auto) {
+ if (supportsAnsi(os.rdbuf())) {
+ setWinColorAnsi(os, value);
+ } else {
+ setWinColorNative(os, value);
+ }
+ } else if (winTermMode() == winTerm::Ansi) {
+ setWinColorAnsi(os, value);
+ } else {
+ setWinColorNative(os, value);
+ }
+ return os;
+ }
+#else
+ template <typename T>
+ inline enableStd<T> setColor(std::ostream& os, T const value) {
+ return os << "\033[" << static_cast<int>(value) << "m";
+ }
+#endif
+} // namespace rang_implementation
+
+template <typename T>
+inline rang_implementation::enableStd<T> operator<<(std::ostream& os, const T value) {
+ const control option = rang_implementation::controlMode();
+ switch (option) {
+ case control::Auto:
+ return rang_implementation::supportsColor() && rang_implementation::isTerminal(os.rdbuf())
+ ? rang_implementation::setColor(os, value)
+ : os;
+ case control::Force:
+ return rang_implementation::setColor(os, value);
+ default:
+ return os;
+ }
+}
+
+inline void setWinTermMode(const rang::winTerm value) noexcept {
+ rang_implementation::winTermMode() = value;
+}
+
+inline void setControlMode(const control value) noexcept {
+ rang_implementation::controlMode() = value;
+}
+
+} // namespace rang
+
+#undef OS_LINUX
+#undef OS_WIN
+#undef OS_MAC
+
+#endif /* ifndef RANG_DOT_HPP */ \ No newline at end of file