From 4d447d3decb132496378d3a332086d974b999814 Mon Sep 17 00:00:00 2001 From: JimmyBinoculars Date: Thu, 26 Feb 2026 18:11:24 +0000 Subject: [PATCH] Proper kernel --- Makefile | 21 ++++- README.md | 67 +++++++++++--- commands.c | 242 +++++++++++++++++++++++++++++++++++++++++++++++++ commands.h | 18 ++++ kernel.c | 140 ++++++++++++++++++---------- string_utils.c | 61 +++++++++++++ string_utils.h | 11 +++ 7 files changed, 490 insertions(+), 70 deletions(-) create mode 100644 commands.c create mode 100644 commands.h create mode 100644 string_utils.c create mode 100644 string_utils.h diff --git a/Makefile b/Makefile index 7492b03..0a7eed1 100644 --- a/Makefile +++ b/Makefile @@ -38,7 +38,7 @@ TARGET = BOOTX64.EFI TARGET_SO = bootx64.so OBJ = $(BUILD_DIR)/main.o KERNEL_TARGET = kernel.elf -KERNEL_OBJ = $(BUILD_DIR)/kernel.o +KERNEL_OBJS = $(BUILD_DIR)/kernel.o $(BUILD_DIR)/string_utils.o $(BUILD_DIR)/commands.o KERNEL_LD = kernel.ld # QEMU settings @@ -76,7 +76,8 @@ QEMU_FLAGS = -machine q35 \ -drive if=pflash,format=raw,file=$(BUILD_DIR)/OVMF_VARS.fd \ -drive format=raw,file=fat:rw:$(BUILD_DIR) \ -net none \ - -nographic + -nographic \ + -no-reboot # Phony targets .PHONY: all clean run setup install-deps @@ -96,15 +97,25 @@ $(BUILD_DIR)/%.o: $(SRC_DIR)/%.c $(CC) $(CFLAGS) -c $< -o $@ # Compile kernel source -$(KERNEL_OBJ): $(SRC_DIR)/kernel.c +$(BUILD_DIR)/kernel.o: $(SRC_DIR)/kernel.c @echo "Compiling kernel.c..." $(CC) -ffreestanding -fno-stack-protector -fno-pic -fshort-wchar \ -mno-red-zone -Wall -Wextra $(EFI_INCLUDES) -c $< -o $@ +$(BUILD_DIR)/string_utils.o: $(SRC_DIR)/string_utils.c + @echo "Compiling string_utils.c..." + $(CC) -ffreestanding -fno-stack-protector -fno-pic -fshort-wchar \ + -mno-red-zone -Wall -Wextra $(EFI_INCLUDES) -c $< -o $@ + +$(BUILD_DIR)/commands.o: $(SRC_DIR)/commands.c + @echo "Compiling commands.c..." + $(CC) -ffreestanding -fno-stack-protector -fno-pic -fshort-wchar \ + -mno-red-zone -Wall -Wextra $(EFI_INCLUDES) -c $< -o $@ + # Link kernel ELF -$(BUILD_DIR)/$(KERNEL_TARGET): $(KERNEL_OBJ) $(KERNEL_LD) +$(BUILD_DIR)/$(KERNEL_TARGET): $(KERNEL_OBJS) $(KERNEL_LD) @echo "Linking kernel ELF..." - $(LD) -nostdlib -T $(KERNEL_LD) $(KERNEL_OBJ) -o $@ + $(LD) -nostdlib -T $(KERNEL_LD) $(KERNEL_OBJS) -o $@ # Link to create shared object $(BUILD_DIR)/$(TARGET_SO): $(OBJ) diff --git a/README.md b/README.md index d09c003..2e1477e 100644 --- a/README.md +++ b/README.md @@ -7,7 +7,8 @@ A minimal bootable UEFI operating system written in C that demonstrates basic UE - **UEFI Boot**: Boots directly on UEFI firmware - **Console I/O**: Interactive keyboard input and screen output - **System Information**: Displays firmware details -- **Simple Commands**: Interactive command interface +- **Kernel Loader**: Loads an ELF64 kernel from the EFI partition +- **Simple Commands**: Minimal interactive command interface ## Requirements @@ -44,13 +45,13 @@ sudo dnf install gnu-efi qemu-system-x86 edk2-ovmf gcc binutils make ## Building -Build the UEFI application: +Build the UEFI application and kernel: ```bash make ``` -This will create `build/EFI/BOOT/BOOTX64.EFI` +This will create `build/EFI/BOOT/BOOTX64.EFI` and `build/kernel.elf`. ## Running @@ -60,24 +61,43 @@ Test with QEMU (no graphics mode): make run ``` +The loader expects `kernel.elf` at the root of the EFI partition (next to the `EFI/` directory). + ### QEMU Controls: - **Exit QEMU**: Press `Ctrl+A`, then `X` -- **Shutdown OS**: Press `q` in the OS +- **Shutdown OS**: Type `shutdown` in the OS ## Commands in the OS Once the OS boots, you can use these commands: -- `h` - Display help -- `i` - Display system information -- `c` - Clear screen -- `q` - Shutdown the system +- `help` - Display all available commands +- `man ` - Display detailed help for a command +- `shutdown` - Shutdown the system +- `clear` - Clear the screen +- `about` - Display system information + +Example usage: +``` +-> help +-> man shutdown +-> clear +-> about +-> shutdown +``` ## Project Structure ``` . -├── main.c # Main UEFI application source code +├── main.c # UEFI loader (reads and loads kernel ELF) +├── kernel.c # Kernel entry point and main loop +├── commands.c # Command registry and handlers +├── commands.h # Command interface definitions +├── string_utils.c # String utility functions +├── string_utils.h # String utility declarations +├── kernel.ld # Kernel linker script +├── boot_info.h # Shared boot interface ├── Makefile # Build configuration └── README.md # This file ``` @@ -87,9 +107,17 @@ Once the OS boots, you can use these commands: 1. **UEFI Entry Point**: The `efi_main` function is the entry point called by UEFI firmware 2. **Initialization**: GNU-EFI library is initialized with the system table 3. **Console Setup**: Output is configured and screen is cleared -4. **Main Loop**: The OS enters a loop waiting for keyboard input -5. **Command Processing**: User commands are processed and executed -6. **Shutdown**: UEFI runtime services are used to shutdown the system +4. **Kernel Load**: The loader reads `kernel.elf` and maps its loadable segments +5. **Main Loop**: The kernel enters a loop waiting for keyboard input +6. **Command Processing**: User commands are parsed and dispatched to registered handlers +7. **Command Execution**: Each command is executed via its handler function +8. **Shutdown**: UEFI runtime services are used to shutdown the system + +**Command System Architecture:** +- Commands are registered in a static array with metadata (name, description, usage, handler) +- The `execute_command()` function parses input, splits command and arguments, and dispatches to handlers +- The `man` command provides detailed help by looking up command metadata +- New commands can be easily added by implementing a handler and registering it ## Building for Real Hardware @@ -99,7 +127,8 @@ To create a bootable USB drive: 2. Format a USB drive with GPT and create an EFI partition (FAT32) 3. Mount the EFI partition 4. Copy `build/EFI/BOOT/BOOTX64.EFI` to `/EFI/BOOT/` on the USB drive -5. Boot from the USB drive in UEFI mode +5. Copy `build/kernel.elf` to the root of the EFI partition +6. Boot from the USB drive in UEFI mode **Warning**: Always backup your data before creating bootable media! @@ -146,11 +175,19 @@ This is a minimal educational example. Feel free to use and modify as needed. ## Further Development -Ideas for expansion: +The codebase is now modular and easy to extend: + +**Adding New Commands:** +1. Open `commands.c` +2. Create a new handler function: `static void cmd_yourcommand(BootInfo *Boot, CHAR16 *Args)` +3. Add your command to the `commands[]` array with name, description, usage, and handler +4. Rebuild with `make` + +**Other Ideas for expansion:** - File system support - Memory management - Graphics output -- Network stack +- Network stack - Device drivers - Multi-tasking diff --git a/commands.c b/commands.c new file mode 100644 index 0000000..67b13c4 --- /dev/null +++ b/commands.c @@ -0,0 +1,242 @@ +#include +#include "commands.h" +#include "string_utils.h" + +#define SAFE_PRINT(Boot, ...) \ + do { \ + if ((Boot) != NULL && (Boot)->print != NULL) { \ + (Boot)->print(__VA_ARGS__); \ + } \ + } while (0) + +// Forward declarations +static void cmd_shutdown(BootInfo *Boot, CHAR16 *Args); +static void cmd_help(BootInfo *Boot, CHAR16 *Args); +static void cmd_man(BootInfo *Boot, CHAR16 *Args); +static void cmd_clear(BootInfo *Boot, CHAR16 *Args); +static void cmd_about(BootInfo *Boot, CHAR16 *Args); + +// Command registry +static Command commands[] = { + { + L"shutdown", + L"Shutdown the system", + L"Usage: shutdown\n\r Initiates a system shutdown using UEFI runtime services.", + cmd_shutdown + }, + { + L"help", + L"Display available commands", + L"Usage: help\n\r Lists all available commands with brief descriptions.", + cmd_help + }, + { + L"man", + L"Display manual page for a command", + L"Usage: man \n\r Shows detailed help for the specified command.", + cmd_man + }, + { + L"clear", + L"Clear the screen", + L"Usage: clear\n\r Clears the console screen.", + cmd_clear + }, + { + L"about", + L"Display system information", + L"Usage: about\n\r Shows information about this operating system.", + cmd_about + }, + {NULL, NULL, NULL, NULL} // Sentinel +}; + +static void request_shutdown(BootInfo *Boot) +{ + if (Boot == NULL) { + return; + } + + if (Boot->shutdown != NULL) { + Boot->shutdown(); + return; + } + + if (Boot->SystemTable != NULL && + Boot->SystemTable->RuntimeServices != NULL && + Boot->SystemTable->RuntimeServices->ResetSystem != NULL) { + Boot->SystemTable->RuntimeServices->ResetSystem( + EfiResetShutdown, EFI_SUCCESS, 0, NULL); + return; + } + + SAFE_PRINT(Boot, L"Shutdown service unavailable.\n\r"); +} + +static void cmd_shutdown(BootInfo *Boot, CHAR16 *Args) +{ + (void)Args; // Unused + SAFE_PRINT(Boot, L"Shutting down...\n\r"); + request_shutdown(Boot); +} + +static void cmd_help(BootInfo *Boot, CHAR16 *Args) +{ + (void)Args; // Unused + show_help(Boot); +} + +static void cmd_man(BootInfo *Boot, CHAR16 *Args) +{ + UINTN i = 0; + + if (Args == NULL || Args[0] == L'\0') { + SAFE_PRINT(Boot, L"Usage: man \n\r"); + SAFE_PRINT(Boot, L"Try 'help' for a list of available commands.\n\r"); + return; + } + + trim_spaces_inplace(Args); + + for (i = 0; commands[i].name != NULL; i++) { + if (ascii_streq_ci(Args, commands[i].name)) { + SAFE_PRINT(Boot, L"\n\r"); + SAFE_PRINT(Boot, L"NAME\n\r"); + SAFE_PRINT(Boot, L" %s - %s\n\r\n\r", commands[i].name, commands[i].description); + SAFE_PRINT(Boot, L"DESCRIPTION\n\r"); + SAFE_PRINT(Boot, L" %s\n\r", commands[i].usage); + SAFE_PRINT(Boot, L"\n\r"); + return; + } + } + + SAFE_PRINT(Boot, L"No manual entry for '%s'\n\r", Args); + SAFE_PRINT(Boot, L"Try 'help' for a list of available commands.\n\r"); +} + +static void cmd_clear(BootInfo *Boot, CHAR16 *Args) +{ + EFI_STATUS Status; + (void)Args; // Unused + + if (Boot != NULL && Boot->clear_screen != NULL) { + Status = Boot->clear_screen(); + if (EFI_ERROR(Status)) { + SAFE_PRINT(Boot, L"Failed to clear screen: %r\n\r", Status); + } + } else { + SAFE_PRINT(Boot, L"Clear screen function unavailable.\n\r"); + } +} + +static void cmd_about(BootInfo *Boot, CHAR16 *Args) +{ + (void)Args; // Unused + + SAFE_PRINT(Boot, L"\n\r"); + SAFE_PRINT(Boot, L"================================================\n\r"); + SAFE_PRINT(Boot, L" Simple UEFI Operating System\n\r"); + SAFE_PRINT(Boot, L"================================================\n\r"); + SAFE_PRINT(Boot, L"\n\r"); + SAFE_PRINT(Boot, L"A minimal bootable UEFI operating system written in C.\n\r"); + SAFE_PRINT(Boot, L"\n\r"); + SAFE_PRINT(Boot, L"Features:\n\r"); + SAFE_PRINT(Boot, L" - UEFI Boot\n\r"); + SAFE_PRINT(Boot, L" - Console I/O\n\r"); + SAFE_PRINT(Boot, L" - ELF64 Kernel Loader\n\r"); + SAFE_PRINT(Boot, L" - Interactive Command Interface\n\r"); + SAFE_PRINT(Boot, L"\n\r"); + SAFE_PRINT(Boot, L"Type 'help' for available commands.\n\r"); + SAFE_PRINT(Boot, L"\n\r"); +} + +void show_help(BootInfo *Boot) +{ + UINTN i = 0; + UINTN max_len = 0; + UINTN name_len = 0; + UINTN padding = 0; + + SAFE_PRINT(Boot, L"\n\r"); + SAFE_PRINT(Boot, L"Available commands:\n\r"); + SAFE_PRINT(Boot, L"\n\r"); + + // Find longest command name for formatting + for (i = 0; commands[i].name != NULL; i++) { + name_len = 0; + while (commands[i].name[name_len] != L'\0') { + name_len++; + } + if (name_len > max_len) { + max_len = name_len; + } + } + + for (i = 0; commands[i].name != NULL; i++) { + SAFE_PRINT(Boot, L" %s", commands[i].name); + + // Add padding + name_len = 0; + while (commands[i].name[name_len] != L'\0') { + name_len++; + } + for (padding = 0; padding < (max_len - name_len + 2); padding++) { + SAFE_PRINT(Boot, L" "); + } + + SAFE_PRINT(Boot, L"- %s\n\r", commands[i].description); + } + + SAFE_PRINT(Boot, L"\n\r"); + SAFE_PRINT(Boot, L"For detailed help on a command, type: man \n\r"); + SAFE_PRINT(Boot, L"\n\r"); +} + +void execute_command(BootInfo *Boot, CHAR16 *Input) +{ + CHAR16 *cmd_start = NULL; + CHAR16 *args_start = NULL; + UINTN i = 0; + + if (Boot == NULL || Input == NULL) { + return; + } + + trim_spaces_inplace(Input); + + if (Input[0] == L'\0') { + return; + } + + // Split command and arguments + cmd_start = Input; + args_start = Input; + + // Find first space + while (*args_start != L'\0' && !is_space16(*args_start)) { + args_start++; + } + + // If we found a space, null-terminate command and advance to args + if (*args_start != L'\0') { + *args_start = L'\0'; + args_start++; + + // Skip leading spaces in args + while (*args_start != L'\0' && is_space16(*args_start)) { + args_start++; + } + } + + // Search for command + for (i = 0; commands[i].name != NULL; i++) { + if (ascii_streq_ci(cmd_start, commands[i].name)) { + commands[i].handler(Boot, args_start); + return; + } + } + + // Command not found + SAFE_PRINT(Boot, L"Unknown command: %s\n\r", cmd_start); + SAFE_PRINT(Boot, L"Type 'help' for a list of available commands.\n\r"); +} diff --git a/commands.h b/commands.h new file mode 100644 index 0000000..5e35f97 --- /dev/null +++ b/commands.h @@ -0,0 +1,18 @@ +#ifndef COMMANDS_H +#define COMMANDS_H + +#include "boot_info.h" + +typedef void (*CommandHandlerFn)(BootInfo *Boot, CHAR16 *Args); + +typedef struct { + const CHAR16 *name; + const CHAR16 *description; + const CHAR16 *usage; + CommandHandlerFn handler; +} Command; + +void execute_command(BootInfo *Boot, CHAR16 *Input); +void show_help(BootInfo *Boot); + +#endif diff --git a/kernel.c b/kernel.c index bb2cbc3..e07b391 100644 --- a/kernel.c +++ b/kernel.c @@ -1,69 +1,109 @@ #include #include "boot_info.h" +#include "commands.h" + +#define SAFE_PRINT(Boot, ...) \ + do { \ + if ((Boot) != NULL && (Boot)->print != NULL) { \ + (Boot)->print(__VA_ARGS__); \ + } \ + } while (0) void kmain(BootInfo *Boot) { EFI_INPUT_KEY Key; + EFI_STATUS Status; + UINTN read_errors = 0; - Boot->clear_screen(); - Boot->set_attribute(EFI_TEXT_ATTR(EFI_LIGHTGREEN, EFI_BLACK)); + if (Boot == NULL) { + return; + } - Boot->print(L"================================================\n\r"); - Boot->print(L" Welcome to Simple UEFI Operating System!\n\r"); - Boot->print(L"================================================\n\r"); - Boot->print(L"\n\r"); - Boot->print(L"System Information:\n\r"); - Boot->print(L" UEFI Firmware Vendor: %s\n\r", Boot->SystemTable->FirmwareVendor); - Boot->print(L" UEFI Firmware Revision: %d.%d\n\r", - Boot->SystemTable->FirmwareRevision >> 16, - Boot->SystemTable->FirmwareRevision & 0xFFFF); - Boot->print(L"\n\r"); + if (Boot->clear_screen != NULL) { + Status = Boot->clear_screen(); + if (EFI_ERROR(Status)) { + SAFE_PRINT(Boot, L"clear_screen failed: %r\n\r", Status); + } + } - Boot->print(L"Available Services:\n\r"); - Boot->print(L" - Console Input/Output: Active\n\r"); - Boot->print(L" - Runtime Services: Active\n\r"); - Boot->print(L" - Boot Services: Active\n\r"); - Boot->print(L"\n\r"); + if (Boot->set_attribute != NULL) { + Status = Boot->set_attribute(EFI_TEXT_ATTR(EFI_LIGHTGREEN, EFI_BLACK)); + if (EFI_ERROR(Status)) { + SAFE_PRINT(Boot, L"set_attribute failed: %r\n\r", Status); + } + } - Boot->print(L"Commands:\n\r"); - Boot->print(L" 'h' - Display help\n\r"); - Boot->print(L" 'i' - Display system info\n\r"); - Boot->print(L" 'c' - Clear screen\n\r"); - Boot->print(L" 'q' - Shutdown\n\r"); - Boot->print(L"\n\r"); - Boot->print(L"Press a key to start...\n\r"); + SAFE_PRINT(Boot, L"================================================\n\r"); + SAFE_PRINT(Boot, L" Welcome to Simple UEFI Operating System!\n\r"); + SAFE_PRINT(Boot, L"================================================\n\r"); + SAFE_PRINT(Boot, L"\n\r"); + SAFE_PRINT(Boot, L"System Information:\n\r"); + if (Boot->SystemTable != NULL) { + SAFE_PRINT(Boot, L" UEFI Firmware Vendor: %s\n\r", + Boot->SystemTable->FirmwareVendor); + SAFE_PRINT(Boot, L" UEFI Firmware Revision: %d.%d\n\r", + Boot->SystemTable->FirmwareRevision >> 16, + Boot->SystemTable->FirmwareRevision & 0xFFFF); + } else { + SAFE_PRINT(Boot, L" UEFI System Table: Unavailable\n\r"); + } + SAFE_PRINT(Boot, L"\n\r"); + + SAFE_PRINT(Boot, L"Available Services:\n\r"); + SAFE_PRINT(Boot, L" - Console Input/Output: %s\n\r", + (Boot->read_key != NULL && Boot->print != NULL) ? L"Active" : L"Unavailable"); + SAFE_PRINT(Boot, L" - Runtime Services: %s\n\r", + (Boot->SystemTable != NULL && + Boot->SystemTable->RuntimeServices != NULL) ? L"Active" : L"Unavailable"); + SAFE_PRINT(Boot, L" - Boot Services: %s\n\r", + (Boot->SystemTable != NULL && + Boot->SystemTable->BootServices != NULL) ? L"Active" : L"Unavailable"); + SAFE_PRINT(Boot, L"\n\r"); + SAFE_PRINT(Boot, L"Type 'help' for a list of commands.\n\r\n\r"); + + // Simple line buffer + CHAR16 line[128]; + UINTN len = 0; + + SAFE_PRINT(Boot, L"-> "); while (TRUE) { - if (EFI_ERROR(Boot->read_key(&Key))) { - continue; + if (Boot->read_key == NULL) { + SAFE_PRINT(Boot, L"Console input unavailable.\n\r"); + break; } - if (Key.UnicodeChar == L'q' || Key.UnicodeChar == L'Q') { - Boot->print(L"\n\rShutting down...\n\r"); - Boot->shutdown(); - } else if (Key.UnicodeChar == L'h' || Key.UnicodeChar == L'H') { - Boot->print(L"\n\r=== Help ===\n\r"); - Boot->print(L"This is a minimal UEFI operating system.\n\r"); - Boot->print(L"It demonstrates basic UEFI functionality.\n\r"); - Boot->print(L"\n\r"); - } else if (Key.UnicodeChar == L'i' || Key.UnicodeChar == L'I') { - Boot->print(L"\n\r=== System Info ===\n\r"); - Boot->print(L"Firmware Vendor: %s\n\r", Boot->SystemTable->FirmwareVendor); - Boot->print(L"Firmware Revision: %d.%d\n\r", - Boot->SystemTable->FirmwareRevision >> 16, - Boot->SystemTable->FirmwareRevision & 0xFFFF); - Boot->print(L"UEFI Specification: %d.%d\n\r", - Boot->SystemTable->Hdr.Revision >> 16, - Boot->SystemTable->Hdr.Revision & 0xFFFF); - Boot->print(L"\n\r"); - } else if (Key.UnicodeChar == L'c' || Key.UnicodeChar == L'C') { - Boot->clear_screen(); - Boot->print(L"Screen cleared. Press 'h' for help.\n\r"); + Status = Boot->read_key(&Key); + if (EFI_ERROR(Status)) { + read_errors++; + if (read_errors == 1 || (read_errors % 64) == 0) { + SAFE_PRINT(Boot, L"read_key failed: %r\n\r", Status); + } + continue; + } + read_errors = 0; + + if (Key.UnicodeChar == L'\r' || Key.UnicodeChar == L'\n') { + // Enter pressed: terminate string and handle + line[len] = L'\0'; + SAFE_PRINT(Boot, L"\n\r"); + execute_command(Boot, line); + + // Reset for next command + len = 0; + SAFE_PRINT(Boot, L"-> "); + } else if (Key.UnicodeChar == L'\b' || Key.UnicodeChar == 0x7F) { + // Backspace + if (len > 0) { + len--; + SAFE_PRINT(Boot, L"\b \b"); + } } else if (Key.UnicodeChar >= 32 && Key.UnicodeChar < 127) { - Boot->print(L"%c", Key.UnicodeChar); - } else if (Key.UnicodeChar == L'\r') { - Boot->print(L"\n\r> "); + if (len < (sizeof(line) / sizeof(line[0]) - 1)) { + line[len++] = Key.UnicodeChar; + SAFE_PRINT(Boot, L"%c", Key.UnicodeChar); + } } } } diff --git a/string_utils.c b/string_utils.c new file mode 100644 index 0000000..377a0a2 --- /dev/null +++ b/string_utils.c @@ -0,0 +1,61 @@ +#include "string_utils.h" + +BOOLEAN is_space16(CHAR16 Ch) +{ + return (Ch == L' ' || Ch == L'\t'); +} + +CHAR16 ascii_lower16(CHAR16 Ch) +{ + if (Ch >= L'A' && Ch <= L'Z') { + return (CHAR16)(Ch + 32); + } + return Ch; +} + +BOOLEAN ascii_streq_ci(const CHAR16 *A, const CHAR16 *B) +{ + if (A == NULL || B == NULL) { + return FALSE; + } + + while (*A != L'\0' && *B != L'\0') { + if (ascii_lower16(*A) != ascii_lower16(*B)) { + return FALSE; + } + A++; + B++; + } + + return (*A == L'\0' && *B == L'\0'); +} + +void trim_spaces_inplace(CHAR16 *Cmd) +{ + UINTN start = 0; + UINTN end = 0; + UINTN i = 0; + + if (Cmd == NULL) { + return; + } + + while (Cmd[start] != L'\0' && is_space16(Cmd[start])) { + start++; + } + + end = start; + while (Cmd[end] != L'\0') { + end++; + } + + while (end > start && is_space16(Cmd[end - 1])) { + end--; + } + + while (i < (end - start)) { + Cmd[i] = Cmd[start + i]; + i++; + } + Cmd[i] = L'\0'; +} diff --git a/string_utils.h b/string_utils.h new file mode 100644 index 0000000..bb89c3d --- /dev/null +++ b/string_utils.h @@ -0,0 +1,11 @@ +#ifndef STRING_UTILS_H +#define STRING_UTILS_H + +#include + +BOOLEAN is_space16(CHAR16 Ch); +CHAR16 ascii_lower16(CHAR16 Ch); +BOOLEAN ascii_streq_ci(const CHAR16 *A, const CHAR16 *B); +void trim_spaces_inplace(CHAR16 *Cmd); + +#endif