diff options
| author | Amlal El Mahrouss <amlal.elmahrouss@icloud.com> | 2025-08-09 08:56:53 +0100 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2025-08-09 08:56:53 +0100 |
| commit | 408be791647c015c99963cc1b6d710f58d729dec (patch) | |
| tree | 35efb24716c331ee6bbe38e6fb0c8ab90079a373 /tools | |
| parent | 9ac7b512ace3f8a3f5613dbf940484a9fa3c82c2 (diff) | |
refactor! rename `tooling` to `tools`
feat: BenchKit improvements and libMsg authorship refactors.
Diffstat (limited to 'tools')
| -rw-r--r-- | tools/dist/.keep | 0 | ||||
| -rw-r--r-- | tools/fsck.hefs.cc | 76 | ||||
| -rw-r--r-- | tools/fsck.hefs.json | 16 | ||||
| -rw-r--r-- | tools/libmkfs/hefs.h | 116 | ||||
| -rw-r--r-- | tools/libmkfs/mkfs.h | 83 | ||||
| -rw-r--r-- | tools/mk_app.py | 80 | ||||
| -rw-r--r-- | tools/mk_fwrk.py | 92 | ||||
| -rw-r--r-- | tools/mk_htman.py | 41 | ||||
| -rw-r--r-- | tools/mk_img.py | 41 | ||||
| -rw-r--r-- | tools/mkfs.hefs.cc | 197 | ||||
| -rw-r--r-- | tools/mkfs.hefs.json | 16 | ||||
| -rw-r--r-- | tools/rang.h | 467 |
12 files changed, 1225 insertions, 0 deletions
diff --git a/tools/dist/.keep b/tools/dist/.keep new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/tools/dist/.keep diff --git a/tools/fsck.hefs.cc b/tools/fsck.hefs.cc new file mode 100644 index 00000000..e3837b8a --- /dev/null +++ b/tools/fsck.hefs.cc @@ -0,0 +1,76 @@ +/* ------------------------------------------- + + Copyright (C) 2025, Amlal El Mahrouss, all rights reserved. + +------------------------------------------- */ + +#include <tools/libmkfs/hefs.h> +#include <tools/libmkfs/mkfs.h> +#include <cstdlib> +#include <fstream> + +static uint16_t kNumericalBase = 10; + +int main(int argc, char** argv) { + if (argc < 2) { + mkfs::console_out() << "fsck: hefs: usage: fsck.hefs -i=<input_device>" + << "\n"; + return EXIT_FAILURE; + } + + auto args = mkfs::detail::build_args(argc, argv); + + auto opt_disk = mkfs::get_option<char>(args, "-i"); + + auto origin = mkfs::get_option<char>(args, "-o"); + + if (opt_disk.empty()) { + mkfs::console_out() << "fsck: hefs: error: HeFS is empty! Exiting..." + << "\n"; + return EXIT_FAILURE; + } + + auto out_origin = 0L; + + if (!mkfs::detail::parse_signed(origin, out_origin, kNumericalBase)) { + mkfs::console_out() << "hefs: error: Invalid -o <dec> argument.\n"; + return EXIT_FAILURE; + } + + std::ifstream output_device(opt_disk, std::ios::binary); + + if (!output_device.good()) { + mkfs::console_out() << "hefs: error: Unable to open output device: " << opt_disk << "\n"; + return EXIT_FAILURE; + } + + output_device.seekg(out_origin); + + if (!output_device.good()) { + mkfs::console_out() << "hefs: error: Failed seek to origin.\n"; + return EXIT_FAILURE; + } + + mkfs::hefs::BootNode boot_node; + + std::memset(&boot_node, 0, sizeof(boot_node)); + + output_device.read(reinterpret_cast<char*>(&boot_node), sizeof(boot_node)); + + if (strncmp(boot_node.magic, kHeFSMagic, kHeFSMagicLen) != 0 || boot_node.sectorCount < 1 || + boot_node.sectorSize < kMkFsSectorSz) { + mkfs::console_out() << "hefs: error: Device is not an HeFS disk: " << opt_disk << "\n"; + return EXIT_FAILURE; + } + + if (boot_node.badSectors > kMkFsMaxBadSectors) { + mkfs::console_out() << "hefs: error: HeFS disk has too much bad sectors: " << opt_disk << "\n"; + return EXIT_FAILURE; + } + + mkfs::console_out() << "hefs: HeFS partition is is healthy, exiting...\n"; + + output_device.close(); + + return EXIT_SUCCESS; +}
\ No newline at end of file diff --git a/tools/fsck.hefs.json b/tools/fsck.hefs.json new file mode 100644 index 00000000..d6ff4b0f --- /dev/null +++ b/tools/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/tools/libmkfs/hefs.h b/tools/libmkfs/hefs.h new file mode 100644 index 00000000..52bb3086 --- /dev/null +++ b/tools/libmkfs/hefs.h @@ -0,0 +1,116 @@ +/* ------------------------------------------- + + 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 kHeFSDefaultVolumeName 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 = 8, +}; + +// Disk status +enum { + kHeFSStatusUnlocked = 0x18, + kHeFSStatusLocked, + kHeFSStatusError, + kHeFSStatusInvalid, + kHeFSStatusCount, +}; + +// Encodings +enum { + kHeFSEncodingFlagsUTF8 = 0x50, + kHeFSEncodingFlagsUTF16, + kHeFSEncodingFlagsUTF32, + kHeFSEncodingFlagsUTF16BE, + kHeFSEncodingFlagsUTF16LE, + kHeFSEncodingFlagsUTF32BE, + kHeFSEncodingFlagsUTF32LE, + kHeFSEncodingFlagsUTF8BE, + kHeFSEncodingFlagsUTF8LE, + kHeFSEncodingFlagsBinary, + kHeFSEncodingFlagsCount = 11, + kHeFSFlagsNone = 0, + kHeFSFlagsReadOnly = 0x100, + kHeFSFlagsHidden, + kHeFSFlagsSystem, + kHeFSFlagsArchive, + kHeFSFlagsDevice, + kHeFSFlagsCount = 7 +}; + +// 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 startBlock{}; + std::uint64_t endBlock{}; + char pad[272]{}; +}; +} // namespace mkfs::hefs diff --git a/tools/libmkfs/mkfs.h b/tools/libmkfs/mkfs.h new file mode 100644 index 00000000..e729c29b --- /dev/null +++ b/tools/libmkfs/mkfs.h @@ -0,0 +1,83 @@ +/* ------------------------------------------- + + Copyright (C) 2025, Amlal El Mahrouss, all rights reserved. + +------------------------------------------- */ + +#pragma once + +#include <tools/rang.h> +#include <sstream> +#include <iostream> +#include <string> + +#define kMkFsSectorSz (512U) +#define kMkFsMaxBadSectors (128U) + +/// @internal +namespace mkfs { + +namespace detail { + /// @internal + /// @brief GB‐to‐byte conversion (use multiplication, not XOR). + inline constexpr size_t gib_cast(uint32_t gb) { + return static_cast<size_t>(gb) * 1024ULL * 1024ULL * 1024ULL; + } + + inline bool parse_decimal(const std::string& opt, unsigned long long& out) { + if (opt.empty()) return false; + char* endptr = nullptr; + unsigned long long val = std::strtoull(opt.c_str(), &endptr, 10); + if (endptr == opt.c_str() || *endptr != '\0') return false; + out = val; + return true; + } + + inline bool parse_signed(const std::string& opt, long& out, int base = 10) { + out = 0L; + + if (opt.empty()) return true; + + char* endptr = nullptr; + long val = std::strtol(opt.c_str(), &endptr, base); + auto err = errno; + + if (err == ERANGE || err == EINVAL) return false; + if (endptr == opt.c_str() || *endptr != '\0') return false; + + out = val; + return true; + } + + inline std::string build_args(int argc, char** argv) { + std::string combined; + for (int i = 1; i < argc; ++i) { + combined += argv[i]; + combined += ' '; + } + return combined; + } +} // namespace detail + +/// @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/tools/mk_app.py b/tools/mk_app.py new file mode 100644 index 00000000..7f7cef17 --- /dev/null +++ b/tools/mk_app.py @@ -0,0 +1,80 @@ +#! /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, + "CommandLine.cc": 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 + proj_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(proj_json_path, 'w') as json_file: + json.dump(manifest, json_file, indent=4) + + proj_cpp_path = os.path.join(base_path, project_name, f"src/CommandLine.cc") + + cpp_file = "#include <libSystem/SystemKit/System.h>\n\nSInt32 _NeMain(SInt32 argc, Char* argv[]) {\n\treturn EXIT_FAILURE;\n}" + + with open(proj_cpp_path, 'w') as cpp_file_io: + cpp_file_io.write(cpp_file) + +if __name__ == "__main__": + if len(sys.argv) != 2: + print("HELP: mk_app.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("INFO: Application created successfully.") diff --git a/tools/mk_fwrk.py b/tools/mk_fwrk.py new file mode 100644 index 00000000..b2ef99ff --- /dev/null +++ b/tools/mk_fwrk.py @@ -0,0 +1,92 @@ +#! /usr/bin/env python3 +# -*- coding: utf-8 -*- + +import os +import json +import sys + +""" + Create directory structure for the framework. +""" +def create_directory_structure(base_path_fwrk, project_file_name, project_name): + # Define the directory structure + structure = { + project_name: { + "headers": { + ".keep": None + }, + "dist": { + ".keep": None + }, + "src": { + ".keep": None, + "DylibMain.cc": None, + }, + "xml": { + ".keep": None + }, + ".keep": None, + f"{project_file_name}.json": {} + } + } + + def create_structure(path, structure_in): + for name, content in structure_in.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_fwrk, exist_ok=True) + create_structure(base_path_fwrk, structure) + + # Create the JSON file + proj_json_path = os.path.join(base_path_fwrk, project_name, f"{project_file_name}.json") + + manifest = { + "compiler_path": "clang++", + "compiler_std": "c++20", + "headers_path": ["./", "../../../dev/kernel", "../../../public/frameworks/", "../../../dev/", "./"], + "sources_path": [ + + ], + "output_name": f"./dist/lib{project_name}.dylib", + "cpp_macros": [ + "kSampleFWVersion=0x0100", + "kSampleFWVersionHighest=0x0100", + "kSampleFWVersionLowest=0x0100", + "__NE_SDK__" + ] + } + + with open(proj_json_path, 'w') as json_file: + json.dump(manifest, json_file, indent=4) + + proj_cpp_path = os.path.join(base_path_fwrk, project_name, f"src/DylibMain.cc") + + cpp_file = "#include <libSystem/SystemKit/System.h>\n\nSInt32 _DylibAttach(SInt32 argc, Char* argv[]) {\n\treturn EXIT_FAILURE;\n}" + + with open(proj_cpp_path, 'w') as cpp_file_io: + cpp_file_io.write(cpp_file) + + xml_blob = f"<PropertyList>\n<PLEntry Type=\"CFString\" Name=\"LibraryName\" Len=\"{len(project_name)}\" Value=\"{project_name}\" /></PropertyList>" + proj_xml_path = os.path.join(base_path_fwrk, project_name, f"xml/app.xml") + + with open(proj_xml_path, 'w') as cpp_file_io: + cpp_file_io.write(xml_blob) + +if __name__ == "__main__": + if len(sys.argv) != 2: + print("HELP: 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], sys.argv[1] + '.fwrk') + + print("INFO: Framework created successfully.") diff --git a/tools/mk_htman.py b/tools/mk_htman.py new file mode 100644 index 00000000..e865f7c5 --- /dev/null +++ b/tools/mk_htman.py @@ -0,0 +1,41 @@ +#! /usr/bin/env python3 +# -*- coding: utf-8 -*- + +import sys, os + +if __name__ == "__main__": + if len(sys.argv) != 2: + print("INFO: mk_htman.py <manual_path>") + sys.exit(os.EX_CONFIG) + + manual_path = sys.argv[1] + if not os.path.exists(manual_path): + print(f"ERROR: Manual path '{manual_path}' does not exist.") + sys.exit(os.EX_NOINPUT) + + if os.path.isdir(manual_path): + print(f"ERROR: Manual path '{manual_path}' is a directory.") + sys.exit(os.EX_NOTDIR) + + if not manual_path.endswith('.man'): + print(f"ERROR: Manual path '{manual_path}' must end with '.man'") + sys.exit(os.EX_DATAERR) + + try: + with open(manual_path, 'r') as file: + content = file.read() + if not content.strip(): + print(f"ERROR: Manual file '{manual_path}' is empty.") + sys.exit(os.EX_DATAERR) + html_content = f"<html><head><title>NeKernel Manual: {manual_path}</title></head><body><pre>{content}</pre></body></html>" + + html_path = manual_path.replace('.man', '.html') + + with open(html_path, 'w') as html_file: + html_file.write(html_content) + except IOError as e: + print(f"ERROR: Could not read manual file '{manual_path}': {e}") + sys.exit(os.EX_IOERR) + + print(f"INFO: Wrote manual '{manual_path}' to HTML.") + sys.exit(os.EX_OK) diff --git a/tools/mk_img.py b/tools/mk_img.py new file mode 100644 index 00000000..f0fa0609 --- /dev/null +++ b/tools/mk_img.py @@ -0,0 +1,41 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- + +import os +import sys +import subprocess +import glob as file_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 = file_glob.glob(os.path.join(source_dir, "*")) + + if not files_to_copy: + print(f"Warning: No files found in {source_dir}, nothing to copy.") + sys.exit(1) + + command = ["mcopy", "-spm", "-i", image_path] + files_to_copy + ["::"] + subprocess.run(command, check=True) + except Exception as e: + print(f"Error: failed: {e}") + sys.exit(1) + +if __name__ == "__main__": + if len(sys.argv) != 3: + print("HELP: 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("INFO: Image created successfully.") diff --git a/tools/mkfs.hefs.cc b/tools/mkfs.hefs.cc new file mode 100644 index 00000000..bf790598 --- /dev/null +++ b/tools/mkfs.hefs.cc @@ -0,0 +1,197 @@ +/* ------------------------------------------- + + Copyright (C) 2025, Amlal El Mahrouss, all rights reserved. + +------------------------------------------- */ + +#include <tools/libmkfs/hefs.h> +#include <tools/libmkfs/mkfs.h> +#include <algorithm> +#include <cstdlib> +#include <cstring> +#include <fstream> +#include <limits> + +static size_t kDiskSize = mkfs::detail::gib_cast(4UL); +static uint16_t kVersion = kHeFSVersion; +static std::u8string kLabel; +static size_t kSectorSize = 512; +static uint16_t kNumericalBase = 10; + +int main(int argc, char** argv) { + if (argc < 2) { + mkfs::console_out() + << "hefs: usage: mkfs.hefs -L=<label> -s=<sector_size> -b=<ind_start> -e=<ind_end> " + "-bs=<block_start> -be=<block_end> -is=<in_start> -ie=<in_end> " + "-S=<disk_size_GB> -o=<output_device>\n"; + + return EXIT_FAILURE; + } + + std::string args = mkfs::detail::build_args(argc, argv); + + auto output_path = mkfs::get_option<char>(args, "-o"); + if (output_path.empty()) { + mkfs::console_out() << "hefs: error: Missing -o <output_device> argument.\n"; + return EXIT_FAILURE; + } + + auto opt_s = mkfs::get_option<char>(args, "-s"); + long parsed_s = 0; + if (!mkfs::detail::parse_signed(opt_s, parsed_s, kNumericalBase) || parsed_s == 0) { + mkfs::console_out() << "hefs: error: Invalid sector size \"" << opt_s + << "\". Must be a positive integer.\n"; + return EXIT_FAILURE; + } + + if ((parsed_s & (parsed_s - 1)) != 0) { + mkfs::console_out() << "hefs: error: Sector size \"" << parsed_s + << "\" is not a power of two.\n"; + return EXIT_FAILURE; + } + kSectorSize = static_cast<size_t>(parsed_s); + + auto opt_L = mkfs::get_option<char>(args, "-L"); + if (!opt_L.empty()) { + kLabel.clear(); + for (char c : opt_L) kLabel.push_back(static_cast<char8_t>(c)); + } else { + kLabel.clear(); + for (size_t i = 0; i < kHeFSPartNameLen && kHeFSDefaultVolumeName[i] != u'\0'; ++i) { + kLabel.push_back(static_cast<char8_t>(kHeFSDefaultVolumeName[i])); + } + } + + auto opt_S = mkfs::get_option<char>(args, "-S"); + unsigned long long gb = 0; + if (!mkfs::detail::parse_decimal(opt_S, gb) || gb == 0ULL) { + mkfs::console_out() << "hefs: error: Invalid disk size \"" << opt_S + << "\". Must be a positive integer.\n"; + return EXIT_FAILURE; + } + unsigned long long max_gb = std::numeric_limits<uint64_t>::max() / (1024ULL * 1024ULL * 1024ULL); + if (gb > max_gb) { + mkfs::console_out() << "hefs: error: Disk size \"" << gb << "GB\" is too large.\n"; + return EXIT_FAILURE; + } + kDiskSize = static_cast<size_t>(gb * 1024ULL * 1024ULL * 1024ULL); + + auto opt_b = mkfs::get_option<char>(args, "-b"); + auto opt_e = mkfs::get_option<char>(args, "-e"); + auto opt_bs = mkfs::get_option<char>(args, "-bs"); + auto opt_be = mkfs::get_option<char>(args, "-be"); + auto opt_is = mkfs::get_option<char>(args, "-is"); + auto opt_ie = mkfs::get_option<char>(args, "-ie"); + + long start_ind = 0, end_ind = 0; + long start_block = 0, end_block = 0; + long start_in = 0, end_in = 0; + + if (!mkfs::detail::parse_signed(opt_b, start_ind, kNumericalBase)) { + mkfs::console_out() << "hefs: error: Invalid -b <dec> argument.\n"; + return EXIT_FAILURE; + } + + if (!mkfs::detail::parse_signed(opt_e, end_ind, kNumericalBase) || end_ind <= start_ind) { + mkfs::console_out() << "hefs: error: Invalid or out-of-range -e <dec> argument.\n"; + return EXIT_FAILURE; + } + + if (!mkfs::detail::parse_signed(opt_bs, start_block, kNumericalBase)) { + mkfs::console_out() << "hefs: error: Invalid -bs <dec> argument.\n"; + return EXIT_FAILURE; + } + + if (!mkfs::detail::parse_signed(opt_be, end_block, kNumericalBase) || end_block <= start_block) { + mkfs::console_out() << "hefs: error: Invalid or out-of-range -be <dec> argument.\n"; + return EXIT_FAILURE; + } + + if (!mkfs::detail::parse_signed(opt_is, start_in, kNumericalBase)) { + mkfs::console_out() << "hefs: error: Invalid -is <dec> argument.\n"; + return EXIT_FAILURE; + } + + if (!mkfs::detail::parse_signed(opt_ie, end_in, kNumericalBase) || end_in <= start_in) { + mkfs::console_out() << "hefs: error: Invalid or out-of-range -ie <dec> argument.\n"; + return EXIT_FAILURE; + } + + if (static_cast<size_t>(end_block) * kSectorSize > kDiskSize || + static_cast<size_t>(end_ind) > kDiskSize || static_cast<size_t>(end_in) > kDiskSize) { + mkfs::console_out() << "hefs: error: One or more ranges exceed disk size.\n"; + return EXIT_FAILURE; + } + + std::ofstream output_device(output_path, std::ios::binary); + + if (!output_device.good()) { + mkfs::console_out() << "hefs: error: Unable to open output device: " << output_path << "\n"; + return EXIT_FAILURE; + } + + mkfs::hefs::BootNode boot_node; + std::memset(&boot_node, 0, sizeof(boot_node)); + + boot_node.version = kVersion; + boot_node.diskKind = mkfs::hefs::kHeFSHardDrive; + boot_node.encoding = mkfs::hefs::kHeFSEncodingFlagsUTF8; + boot_node.diskSize = kDiskSize; + boot_node.sectorSize = kSectorSize; + boot_node.sectorCount = kDiskSize / kSectorSize; + boot_node.startIND = static_cast<size_t>(start_ind) + sizeof(mkfs::hefs::BootNode); + boot_node.endIND = static_cast<size_t>(end_ind); + boot_node.startIN = static_cast<size_t>(start_in); + boot_node.endIN = static_cast<size_t>(end_in); + boot_node.startBlock = static_cast<size_t>(start_block); + boot_node.endBlock = static_cast<size_t>(end_block); + boot_node.indCount = 0UL; + boot_node.diskStatus = mkfs::hefs::kHeFSStatusUnlocked; + + static_assert(sizeof(boot_node.magic) >= kHeFSMagicLen, + "BootNode::magic too small to hold kHeFSMagicLen"); + + std::memset(boot_node.magic, 0, sizeof(boot_node.magic)); + size_t magic_copy = + (sizeof(boot_node.magic) < kHeFSMagicLen - 1) ? sizeof(boot_node.magic) : (kHeFSMagicLen - 1); + std::memcpy(boot_node.magic, kHeFSMagic, magic_copy); + boot_node.magic[magic_copy] = 0; + + constexpr size_t vol_slots = kHeFSPartNameLen; + + std::memset(boot_node.volumeName, 0, sizeof(boot_node.volumeName)); + + size_t label_units = std::min(kLabel.size(), vol_slots - 1); + + for (size_t i = 0; i < label_units; ++i) { + boot_node.volumeName[i] = static_cast<char8_t>(kLabel[i]); + } + + boot_node.volumeName[label_units] = 0U; + + output_device.seekp(static_cast<std::streamoff>(start_ind)); + if (!output_device.good()) { + mkfs::console_out() << "hefs: error: Failed seek to index start.\n"; + return EXIT_FAILURE; + } + + output_device.write(reinterpret_cast<const char*>(&boot_node), sizeof(boot_node)); + if (!output_device.good()) { + mkfs::console_out() << "hefs: error: Unable to write BootNode to output device: " << output_path + << "\n"; + return EXIT_FAILURE; + } + + output_device.seekp(static_cast<std::streamoff>(boot_node.startIND)); + if (!output_device.good()) { + mkfs::console_out() << "hefs: error: Failed seek to startIND.\n"; + return EXIT_FAILURE; + } + + output_device.flush(); + output_device.close(); + + mkfs::console_out() << "hefs: info: Wrote filesystem to output device: " << output_path << "\n"; + + return EXIT_SUCCESS; +} diff --git a/tools/mkfs.hefs.json b/tools/mkfs.hefs.json new file mode 100644 index 00000000..d29b7f73 --- /dev/null +++ b/tools/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/tools/rang.h b/tools/rang.h new file mode 100644 index 00000000..5d1c68ef --- /dev/null +++ b/tools/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 |
