├── .gitignore ├── LICENSE ├── Makefile ├── README.md ├── bin ├── clear.c ├── cowsay.c ├── cpuinfo.c ├── date.c ├── echo.c ├── expr.c ├── factor.c ├── fetch.c ├── grep.c ├── help.c ├── hexdump.c ├── meminfo.c ├── panic.c ├── rand.c ├── reboot.c ├── shutdown.c ├── sleep.c ├── time.c ├── tty.c ├── uname.c ├── uptime.c ├── watch.c ├── whoami.c └── yes.c ├── drivers ├── keyboard │ └── kb.c ├── shell │ └── shell.c └── video │ └── vga.c ├── include ├── boot │ └── multiboot.h ├── kernel │ ├── arch │ │ └── x86 │ │ │ └── cpu.h │ ├── cdefs.h │ ├── panic │ │ ├── boot.h │ │ ├── debug.h │ │ └── panic.h │ ├── ports │ │ └── ports.h │ ├── rtc │ │ └── rtc.h │ └── syscall │ │ └── syscall.h ├── keyboard │ └── kb.h ├── lib │ ├── stdbool.h │ ├── stddef.h │ └── string.h ├── mm │ └── vmm.h ├── shell │ └── shell.h ├── version │ └── version.h └── video │ └── vga.h ├── init ├── boot.s └── main.c ├── isodir └── boot │ └── grub │ └── grub.cfg ├── lib └── libc │ └── string │ └── string.c ├── linker.ld ├── mm └── vmm.c ├── sys ├── arch │ └── x86 │ │ └── cpu.c ├── panic │ ├── boot.c │ ├── debug.c │ └── panic.c ├── rtc │ └── rtc.c └── syscall │ └── syscall.c └── usr.bin ├── false.c └── true.c /.gitignore: -------------------------------------------------------------------------------- 1 | # Build files 2 | obj/ 3 | obj/* 4 | isodir/boot/*.elf 5 | *.iso 6 | *.elf 7 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | BSD 3-Clause License 2 | 3 | Copyright (c) 2025, 0x16000 4 | 5 | Redistribution and use in source and binary forms, with or without 6 | modification, are permitted provided that the following conditions are met: 7 | 8 | 1. Redistributions of source code must retain the above copyright notice, this 9 | list of conditions and the following disclaimer. 10 | 11 | 2. Redistributions in binary form must reproduce the above copyright notice, 12 | this list of conditions and the following disclaimer in the documentation 13 | and/or other materials provided with the distribution. 14 | 15 | 3. Neither the name of the copyright holder nor the names of its 16 | contributors may be used to endorse or promote products derived from 17 | this software without specific prior written permission. 18 | 19 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 20 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 21 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 22 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 23 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 24 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 25 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 26 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 27 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 28 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 29 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | # Compiler and linker settings 2 | CC = gcc 3 | AS = nasm 4 | LD = ld 5 | CFLAGS = -m32 -ffreestanding -fno-stack-protector -Iinclude 6 | ASFLAGS = -f elf32 7 | LDFLAGS = -m elf_i386 -T linker.ld 8 | ISO_TOOL = grub-mkrescue 9 | QEMU = qemu-system-x86_64 10 | 11 | # Directories 12 | SRC_DIR = . 13 | OBJ_DIR = obj 14 | ISO_DIR = isodir 15 | BOOT_DIR = $(ISO_DIR)/boot 16 | BIN_DIR = bin 17 | INCLUDE_DIR = include 18 | USR_BIN_DIR = usr.bin 19 | 20 | # Source files 21 | SRCS = \ 22 | init/main.c \ 23 | drivers/video/vga.c \ 24 | drivers/keyboard/kb.c \ 25 | drivers/shell/shell.c \ 26 | lib/libc/string/string.c \ 27 | sys/syscall/syscall.c \ 28 | sys/rtc/rtc.c \ 29 | sys/panic/panic.c \ 30 | sys/panic/boot.c \ 31 | sys/arch/x86/cpu.c \ 32 | sys/panic/debug.c \ 33 | mm/vmm.c 34 | 35 | # Bin folder source files 36 | BIN_SRCS = \ 37 | $(BIN_DIR)/clear.c \ 38 | $(BIN_DIR)/echo.c \ 39 | $(BIN_DIR)/cpuinfo.c \ 40 | $(BIN_DIR)/reboot.c \ 41 | $(BIN_DIR)/shutdown.c \ 42 | $(BIN_DIR)/time.c \ 43 | $(BIN_DIR)/date.c \ 44 | $(BIN_DIR)/uptime.c \ 45 | $(BIN_DIR)/help.c \ 46 | $(BIN_DIR)/whoami.c \ 47 | $(BIN_DIR)/meminfo.c \ 48 | $(BIN_DIR)/cowsay.c \ 49 | $(BIN_DIR)/yes.c \ 50 | $(BIN_DIR)/uname.c \ 51 | $(BIN_DIR)/fetch.c \ 52 | $(BIN_DIR)/sleep.c \ 53 | $(BIN_DIR)/hexdump.c \ 54 | $(BIN_DIR)/expr.c \ 55 | $(BIN_DIR)/grep.c \ 56 | $(BIN_DIR)/factor.c \ 57 | $(BIN_DIR)/rand.c \ 58 | $(BIN_DIR)/tty.c \ 59 | $(BIN_DIR)/watch.c \ 60 | $(USR_BIN_DIR)/true.c \ 61 | $(USR_BIN_DIR)/false.c 62 | 63 | # Object files 64 | OBJS = $(patsubst %.c, $(OBJ_DIR)/%.o, $(SRCS)) 65 | BIN_OBJS = $(patsubst %.c, $(OBJ_DIR)/%.o, $(BIN_SRCS)) 66 | BOOT_OBJ = $(OBJ_DIR)/boot.o 67 | 68 | # Output files 69 | KERNEL_ELF = kernel.elf 70 | ISO_IMAGE = bunix.iso 71 | 72 | # Default target 73 | all: $(ISO_IMAGE) 74 | 75 | # Rule to compile source files into object files 76 | $(OBJ_DIR)/%.o: $(SRC_DIR)/%.c 77 | @mkdir -p $(dir $@) 78 | $(CC) $(CFLAGS) -I$(INCLUDE_DIR) -c $< -o $@ 79 | 80 | # Rule to compile bin folder source files into object files 81 | $(OBJ_DIR)/$(BIN_DIR)/%.o: $(BIN_DIR)/%.c 82 | @mkdir -p $(dir $@) 83 | $(CC) $(CFLAGS) -I$(INCLUDE_DIR) -c $< -o $@ 84 | 85 | # Rule to assemble boot.s into an object file 86 | $(BOOT_OBJ): init/boot.s 87 | @mkdir -p $(OBJ_DIR) 88 | $(AS) $(ASFLAGS) $< -o $@ 89 | 90 | # Rule to link object files into the kernel ELF 91 | $(KERNEL_ELF): $(BOOT_OBJ) $(OBJS) $(BIN_OBJS) 92 | $(LD) $(LDFLAGS) -o $@ $^ 93 | @mkdir -p $(BOOT_DIR) 94 | cp $(KERNEL_ELF) $(BOOT_DIR)/$(KERNEL_ELF) 95 | 96 | # Rule to create the ISO image 97 | $(ISO_IMAGE): $(KERNEL_ELF) 98 | $(ISO_TOOL) -o $@ $(ISO_DIR) 99 | 100 | # Rule to run the ISO in QEMU 101 | run: $(ISO_IMAGE) 102 | $(QEMU) -enable-kvm -cdrom $(ISO_IMAGE) -m 1024 103 | 104 | # Clean up build artifacts 105 | clean: 106 | rm -rf $(OBJ_DIR) $(KERNEL_ELF) $(ISO_IMAGE) $(BOOT_DIR)/$(KERNEL_ELF) 107 | 108 | .PHONY: all clean run 109 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ![Bunix Logo](https://github.com/user-attachments/assets/ceaf6f84-0ba8-4df5-b7ea-014fb0951aaf) 2 | 3 | # Bunix 4 | 5 | **Bunix** is a Unix-like operating system developed entirely from scratch by a single developer. 6 | Focusing on **performance**, **stability**, and **security**, Bunix is an ambitious project to build a modern OS from the ground up. 7 | 8 | > 🚧 Development may slow down occasionally due to school and personal life commitments. 9 | 10 | --- 11 | 12 | ## 🖥️ System Requirements 13 | 14 | - **Architecture Support**: Currently supports **i386** (32-bit). 15 | Support for **x86_64** (64-bit) is planned for future releases. 16 | 17 | - **Boot Method**: BIOS only (for now). 18 | **UEFI** support is also planned, though it currently works with **UEFI CSM (Compatibility Support Module)**. 19 | 20 | --- 21 | 22 | ## 🤝 Contributing 23 | 24 | Interested in contributing to Bunix? Awesome! 25 | Here’s what we expect from contributors: 26 | 27 | 1. Write code (obviously 😄). 28 | 2. **Test your changes** and provide a screenshot or demo showing that it works. 29 | 3. Clearly explain what your contribution does (e.g., new syscalls, keyboard drivers, improvements). 30 | 4. Unverified or vague contributions will not be accepted. 31 | 32 | --- 33 | 34 | ## 🛠️ Building from Source 35 | 36 | Make sure you have the required dependencies installed: 37 | 38 | 1. `sudo apt-get update && sudo apt-get install qemu-system nasm mtools gcc-multilib xorriso` 39 | 2. Build: `make` 40 | 3. After building a `bunix.iso` file will be provided in the project's root directory 41 | 42 | ## Build and Run 43 | 1. Execute: `make run` 44 | 45 | # Future of Bunix 46 | This is definitely Fun to Work on and Will improve over time! 47 | We will have to look and see. 48 | 49 | -------------------------------------------------------------------------------- /bin/clear.c: -------------------------------------------------------------------------------- 1 | #include "../include/shell/shell.h" 2 | #include "../include/video/vga.h" 3 | 4 | void clear_command(const char *args) { 5 | vga_clear(); 6 | } 7 | -------------------------------------------------------------------------------- /bin/cowsay.c: -------------------------------------------------------------------------------- 1 | // cowsay_command 2 | #include "../include/shell/shell.h" 3 | #include "../include/video/vga.h" 4 | #include "../include/lib/string.h" 5 | 6 | char cowsays[100] = "\n\n" 7 | " ^__^\n" 8 | " (oo)_______\n" 9 | " (__) )>\n" 10 | " II---w II\n" 11 | " II II\n"; 12 | 13 | void cowsay_command(const char *args) { 14 | int arg_len = 0; 15 | 16 | // Check if arguments are NULL or empty 17 | if (args == NULL || *args == '\0') { 18 | vga_puts("cowsay: missing argument\nUsage: cowsay \n"); 19 | return; 20 | } 21 | 22 | // Skip leading whitespace 23 | while (*args == ' ') args++; 24 | 25 | // Check if the entire input is whitespace 26 | if (*args == '\0') { 27 | vga_putchar('\n'); 28 | return; 29 | } 30 | 31 | vga_puts("< "); 32 | 33 | // Handle quoted arguments (both single and double quotes) 34 | char quote = '\0'; 35 | if (*args == '\'' || *args == '"') { 36 | quote = *args; 37 | args++; // Move past the opening quote 38 | } 39 | 40 | // Print characters until the closing quote or end of string 41 | while (*args != '\0' && (quote ? *args != quote : 1)) { 42 | vga_putchar(*args); 43 | arg_len++; 44 | args++; 45 | } 46 | 47 | // If closing quote is missing 48 | if (quote && *args != quote) { 49 | vga_puts("\necho: missing closing quote\n"); 50 | return; 51 | } 52 | 53 | args++; // Move past the closing quote, if present 54 | vga_puts(" >\n"); 55 | 56 | // Add a corresponding amount of dashes(-) 57 | for (int i = 0; i < arg_len + 4; i++) { 58 | vga_putchar('-'); 59 | } 60 | 61 | vga_puts("\n"); 62 | vga_puts(cowsays); 63 | vga_putchar('\n'); 64 | } 65 | -------------------------------------------------------------------------------- /bin/cpuinfo.c: -------------------------------------------------------------------------------- 1 | // cpuinfo.c 2 | #include "../include/shell/shell.h" 3 | #include "../include/video/vga.h" 4 | #include "../include/kernel/arch/x86/cpu.h" 5 | #include 6 | 7 | void print_cpu_header(const char* text) { 8 | vga_set_color(VGA_COLOR_WHITE, VGA_COLOR_BLUE); 9 | vga_puts(" "); 10 | vga_puts(text); 11 | vga_puts(" "); 12 | vga_set_color(VGA_COLOR_BLACK, VGA_COLOR_LIGHT_GREY); 13 | } 14 | 15 | void print_cpu_row(const char* label, const char* value) { 16 | vga_set_color(VGA_COLOR_CYAN, VGA_COLOR_BLACK); 17 | vga_puts(" "); 18 | vga_puts(label); 19 | vga_puts(": "); 20 | vga_set_color(VGA_COLOR_WHITE, VGA_COLOR_BLACK); 21 | vga_puts(value); 22 | vga_puts("\n"); 23 | } 24 | 25 | void print_feature(cpu_info_t* cpu, const char* label, bool feature) { 26 | vga_set_color(feature ? VGA_COLOR_GREEN : VGA_COLOR_DARK_GREY, VGA_COLOR_BLACK); 27 | vga_puts(feature ? " ✓ " : " ✗ "); 28 | vga_set_color(VGA_COLOR_WHITE, VGA_COLOR_BLACK); 29 | vga_puts(label); 30 | vga_puts(" "); 31 | } 32 | 33 | // Simple number to string conversion 34 | static void itoa(int num, char* str, int base) { 35 | int i = 0; 36 | int is_negative = 0; 37 | 38 | if (num == 0) { 39 | str[i++] = '0'; 40 | str[i] = '\0'; 41 | return; 42 | } 43 | 44 | if (num < 0 && base == 10) { 45 | is_negative = 1; 46 | num = -num; 47 | } 48 | 49 | while (num != 0) { 50 | int rem = num % base; 51 | str[i++] = (rem > 9) ? (rem - 10) + 'a' : rem + '0'; 52 | num = num / base; 53 | } 54 | 55 | if (is_negative) 56 | str[i++] = '-'; 57 | 58 | str[i] = '\0'; 59 | 60 | // Reverse the string 61 | int start = 0; 62 | int end = i - 1; 63 | while (start < end) { 64 | char tmp = str[start]; 65 | str[start] = str[end]; 66 | str[end] = tmp; 67 | start++; 68 | end--; 69 | } 70 | } 71 | 72 | void cpuinfo_command(const char *args) { 73 | cpu_info_t cpu; 74 | cpu_identify(&cpu); 75 | uint8_t original_color = vga_get_color(); 76 | 77 | // Main header 78 | vga_set_color(VGA_COLOR_YELLOW, VGA_COLOR_BLACK); 79 | vga_puts("╔════════════════════════════════════════════╗\n"); 80 | vga_puts("║ "); 81 | print_cpu_header("PROCESSOR INFORMATION"); 82 | vga_puts(" ║\n"); 83 | vga_puts("╚════════════════════════════════════════════╝\n"); 84 | 85 | // Basic information 86 | print_cpu_row("Vendor ID ", cpu.identity.vendor_id); 87 | print_cpu_row("Brand String ", cpu.identity.brand_string); 88 | print_cpu_row("Architecture ", cpu.features.lm ? "x86_64" : "i386"); 89 | 90 | // Format family/model/stepping manually 91 | char buf[32]; 92 | char num_buf[12]; 93 | 94 | // Family 95 | itoa(cpu.identity.family, num_buf, 10); 96 | strcpy(buf, num_buf); 97 | strcat(buf, "/"); 98 | 99 | // Model 100 | itoa(cpu.identity.model, num_buf, 10); 101 | strcat(buf, num_buf); 102 | strcat(buf, "/"); 103 | 104 | // Stepping 105 | itoa(cpu.identity.stepping, num_buf, 10); 106 | strcat(buf, num_buf); 107 | 108 | print_cpu_row("Family/Model ", buf); 109 | 110 | // TSC Frequency 111 | uint32_t mhz = cpu.tsc_frequency / 1000; 112 | itoa(mhz, num_buf, 10); 113 | strcpy(buf, num_buf); 114 | strcat(buf, " MHz"); 115 | print_cpu_row("TSC Frequency", buf); 116 | 117 | // Features 118 | vga_set_color(VGA_COLOR_WHITE, VGA_COLOR_BLUE); 119 | vga_puts(" INSTRUCTION SETS "); 120 | vga_set_color(VGA_COLOR_BLACK, VGA_COLOR_LIGHT_GREY); 121 | vga_puts("\n"); 122 | 123 | print_feature(&cpu, "FPU ", cpu.features.fpu); 124 | print_feature(&cpu, "SSE4.2 ", cpu.features.sse4_2); 125 | print_feature(&cpu, "AVX2 ", cpu.features.avx2); 126 | vga_puts("\n"); 127 | print_feature(&cpu, "AES-NI ", cpu.features.aes); 128 | print_feature(&cpu, "AVX512F ", cpu.features.avx512f); 129 | vga_puts("\n\n"); 130 | 131 | // Cache information 132 | vga_set_color(VGA_COLOR_WHITE, VGA_COLOR_BLUE); 133 | vga_puts(" CACHE HIERARCHY "); 134 | vga_set_color(VGA_COLOR_BLACK, VGA_COLOR_LIGHT_GREY); 135 | vga_puts("\n"); 136 | 137 | for (uint8_t i = 0; i < cpu.num_caches; i++) { 138 | const cache_descriptor_t* c = &cpu.caches[i]; 139 | vga_set_color(VGA_COLOR_CYAN, VGA_COLOR_BLACK); 140 | vga_puts(" L"); 141 | vga_putdec(c->level, 0); 142 | 143 | vga_set_color(VGA_COLOR_WHITE, VGA_COLOR_BLACK); 144 | vga_puts(" "); 145 | vga_puts(c->type == CACHE_TYPE_DATA ? "Data " : 146 | c->type == CACHE_TYPE_INSTRUCTION ? "Instr " : "Unified "); 147 | 148 | vga_puts(" "); 149 | vga_putdec(c->size / 1024, 4); 150 | vga_puts(" KB"); 151 | vga_puts("\n"); 152 | } 153 | 154 | vga_set_color(original_color & 0x0F, (original_color >> 4) & 0x0F); 155 | } 156 | -------------------------------------------------------------------------------- /bin/date.c: -------------------------------------------------------------------------------- 1 | #include "../include/shell/shell.h" 2 | #include "../include/video/vga.h" 3 | #include "../include/kernel/rtc/rtc.h" 4 | 5 | // Constants for weekday names 6 | static const char *weekday_names[] = {"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"}; 7 | 8 | void date_command(const char *args) { 9 | // Ignore unused arguments 10 | (void)args; 11 | 12 | struct rtc_date date; 13 | rtc_read_full(&date); // Assuming this populates the `date` struct 14 | 15 | // Print the date in DD/MM/YYYY format 16 | vga_puts("Date: "); 17 | vga_putdec(date.day, 2); 18 | vga_putchar('/'); 19 | vga_putdec(date.month, 2); 20 | vga_putchar('/'); 21 | vga_putdec(date.year, 4); 22 | 23 | // Calculate the weekday using the function from rtc.c 24 | uint8_t weekday = day_of_week(date.day, date.month, date.year); 25 | 26 | // Print the weekday 27 | vga_puts(" ("); 28 | if (weekday < 7) { 29 | vga_puts(weekday_names[weekday]); 30 | } else { 31 | vga_puts("???"); 32 | } 33 | vga_puts(")\n"); 34 | } 35 | -------------------------------------------------------------------------------- /bin/echo.c: -------------------------------------------------------------------------------- 1 | #include "../include/shell/shell.h" 2 | #include "../include/video/vga.h" 3 | #include "../include/lib/string.h" 4 | 5 | void echo_command(const char *args) { 6 | // Check if arguments are NULL or empty 7 | if (args == NULL || *args == '\0') { 8 | vga_puts("echo: missing argument\nUsage: echo \n"); 9 | return; 10 | } 11 | 12 | // Skip leading whitespace 13 | while (*args == ' ') args++; 14 | 15 | // Check if the entire input is whitespace 16 | if (*args == '\0') { 17 | vga_putchar('\n'); 18 | return; 19 | } 20 | 21 | // Handle quoted arguments (both single and double quotes) 22 | if (*args == '\'' || *args == '"') { 23 | char quote = *args; 24 | args++; // Move past the opening quote 25 | 26 | // Print characters until the closing quote is found 27 | while (*args != '\0' && *args != quote) { 28 | vga_putchar(*args); // Use vga_putchar instead of vga_putc 29 | args++; 30 | } 31 | 32 | // If the closing quote is missing, print an error 33 | if (*args != quote) { 34 | vga_puts("\necho: missing closing quote\n"); 35 | return; 36 | } 37 | 38 | args++; // Move past the closing quote 39 | } else { 40 | // Print the rest of the arguments as-is 41 | while (*args != '\0') { 42 | vga_putchar(*args); // Use vga_putchar instead of vga_putc 43 | args++; 44 | } 45 | } 46 | 47 | vga_putchar('\n'); // Use vga_putchar for newline 48 | } 49 | -------------------------------------------------------------------------------- /bin/expr.c: -------------------------------------------------------------------------------- 1 | #include "../include/shell/shell.h" 2 | #include "../include/video/vga.h" 3 | #include "../include/lib/string.h" 4 | 5 | // Custom atoi implementation 6 | int simple_atoi(const char *str) { 7 | int result = 0; 8 | int sign = 1; 9 | 10 | // Handle leading whitespace 11 | while (*str == ' ') str++; 12 | 13 | // Handle optional sign 14 | if (*str == '-') { 15 | sign = -1; 16 | str++; 17 | } else if (*str == '+') { 18 | str++; 19 | } 20 | 21 | // Convert digits to integer 22 | while (*str >= '0' && *str <= '9') { 23 | result = result * 10 + (*str - '0'); 24 | str++; 25 | } 26 | 27 | return sign * result; 28 | } 29 | 30 | // Custom itoa implementation 31 | void simple_itoa(int num, char *buffer) { 32 | int i = 0; 33 | int is_negative = 0; 34 | 35 | // Handle 0 explicitly 36 | if (num == 0) { 37 | buffer[i++] = '0'; 38 | buffer[i] = '\0'; 39 | return; 40 | } 41 | 42 | // Handle negative numbers 43 | if (num < 0) { 44 | is_negative = 1; 45 | num = -num; 46 | } 47 | 48 | // Process individual digits 49 | while (num != 0) { 50 | int rem = num % 10; 51 | buffer[i++] = rem + '0'; 52 | num = num / 10; 53 | } 54 | 55 | // Add negative sign if needed 56 | if (is_negative) { 57 | buffer[i++] = '-'; 58 | } 59 | 60 | buffer[i] = '\0'; 61 | 62 | // Reverse the string 63 | int start = 0; 64 | int end = i - 1; 65 | while (start < end) { 66 | char temp = buffer[start]; 67 | buffer[start] = buffer[end]; 68 | buffer[end] = temp; 69 | start++; 70 | end--; 71 | } 72 | } 73 | 74 | void expr_command(const char *args) { 75 | if (args == NULL || *args == '\0') { 76 | vga_puts("expr: missing arguments\nUsage: expr NUM1 OP NUM2\n"); 77 | return; 78 | } 79 | 80 | // Skip leading whitespace 81 | while (*args == ' ') args++; 82 | 83 | // Parse first number 84 | const char *num1_start = args; 85 | while (*args >= '0' && *args <= '9') args++; 86 | if (args == num1_start) { 87 | vga_puts("expr: invalid first number\n"); 88 | return; 89 | } 90 | char num1_buf[32]; 91 | strncpy(num1_buf, num1_start, args - num1_start); 92 | num1_buf[args - num1_start] = '\0'; 93 | int num1 = simple_atoi(num1_buf); 94 | 95 | // Skip whitespace before operator 96 | while (*args == ' ') args++; 97 | 98 | // Parse operator 99 | if (*args == '\0') { 100 | vga_puts("expr: missing operator\n"); 101 | return; 102 | } 103 | char op = *args++; 104 | 105 | // Skip whitespace after operator 106 | while (*args == ' ') args++; 107 | 108 | // Parse second number 109 | const char *num2_start = args; 110 | while (*args >= '0' && *args <= '9') args++; 111 | if (args == num2_start) { 112 | vga_puts("expr: invalid second number\n"); 113 | return; 114 | } 115 | char num2_buf[32]; 116 | strncpy(num2_buf, num2_start, args - num2_start); 117 | num2_buf[args - num2_start] = '\0'; 118 | int num2 = simple_atoi(num2_buf); 119 | 120 | // Perform calculation 121 | int result; 122 | switch (op) { 123 | case '+': 124 | result = num1 + num2; 125 | break; 126 | case '-': 127 | result = num1 - num2; 128 | break; 129 | case '*': 130 | result = num1 * num2; 131 | break; 132 | case '/': 133 | if (num2 == 0) { 134 | vga_puts("expr: division by zero\n"); 135 | return; 136 | } 137 | result = num1 / num2; 138 | break; 139 | case '%': 140 | if (num2 == 0) { 141 | vga_puts("expr: division by zero\n"); 142 | return; 143 | } 144 | result = num1 % num2; 145 | break; 146 | case '=': 147 | result = (num1 == num2); 148 | break; 149 | case '!': 150 | if (*args++ != '=') { 151 | vga_puts("expr: unknown operator\n"); 152 | return; 153 | } 154 | result = (num1 != num2); 155 | break; 156 | case '>': 157 | result = (num1 > num2); 158 | break; 159 | case '<': 160 | result = (num1 < num2); 161 | break; 162 | default: 163 | vga_puts("expr: unknown operator\n"); 164 | return; 165 | } 166 | 167 | // Print result 168 | char buf[32]; 169 | simple_itoa(result, buf); 170 | vga_puts(buf); 171 | vga_putchar('\n'); 172 | } 173 | -------------------------------------------------------------------------------- /bin/factor.c: -------------------------------------------------------------------------------- 1 | #include "../include/shell/shell.h" 2 | #include "../include/video/vga.h" 3 | #include "../include/lib/string.h" 4 | 5 | // Helper function to convert unsigned long to string 6 | static void ul_to_str(unsigned long num, char *str) { 7 | char tmp[20]; 8 | int i = 0; 9 | 10 | if (num == 0) { 11 | str[0] = '0'; 12 | str[1] = '\0'; 13 | return; 14 | } 15 | 16 | while (num > 0) { 17 | tmp[i++] = '0' + (num % 10); 18 | num /= 10; 19 | } 20 | 21 | for (int j = 0; j < i; j++) { 22 | str[j] = tmp[i - j - 1]; 23 | } 24 | str[i] = '\0'; 25 | } 26 | 27 | void factor_command(const char *args) { 28 | // Check if arguments are NULL or empty 29 | if (args == NULL || *args == '\0') { 30 | vga_puts("factor: missing argument\nUsage: factor \n"); 31 | return; 32 | } 33 | 34 | // Skip leading whitespace 35 | while (*args == ' ') args++; 36 | 37 | // Check if the input is a valid number 38 | const char *ptr = args; 39 | while (*ptr) { 40 | if (*ptr < '0' || *ptr > '9') { 41 | vga_puts("factor: invalid number\n"); 42 | return; 43 | } 44 | ptr++; 45 | } 46 | 47 | unsigned long num = 0; 48 | ptr = args; 49 | while (*ptr) { 50 | num = num * 10 + (*ptr - '0'); 51 | if (num < (unsigned long)(*ptr - '0')) { // Check for overflow 52 | vga_puts("factor: number too large\n"); 53 | return; 54 | } 55 | ptr++; 56 | } 57 | 58 | if (num < 2) { 59 | vga_puts("factor: number must be greater than 1\n"); 60 | return; 61 | } 62 | 63 | // Print the original number 64 | vga_puts(args); 65 | vga_puts(": "); 66 | 67 | // Factor the number 68 | int first = 1; 69 | unsigned long n = num; 70 | char buf[20]; 71 | 72 | // Check for 2 separately 73 | while (n % 2 == 0) { 74 | if (!first) vga_puts(" "); 75 | vga_puts("2"); 76 | n /= 2; 77 | first = 0; 78 | } 79 | 80 | // Check odd divisors 81 | for (unsigned long i = 3; i <= n; i += 2) { 82 | while (n % i == 0) { 83 | if (!first) vga_puts(" "); 84 | ul_to_str(i, buf); 85 | vga_puts(buf); 86 | n /= i; 87 | first = 0; 88 | } 89 | } 90 | 91 | vga_putchar('\n'); 92 | } 93 | -------------------------------------------------------------------------------- /bin/fetch.c: -------------------------------------------------------------------------------- 1 | #include