]> Projects (at) Tadryanom (dot) Me - AdrOS.git/commitdiff
feat: core utilities (cat, ls, mkdir, rm)
authorTulio A M Mendes <[email protected]>
Wed, 11 Feb 2026 22:27:23 +0000 (19:27 -0300)
committerTulio A M Mendes <[email protected]>
Fri, 13 Feb 2026 02:20:50 +0000 (23:20 -0300)
- user/cat.c: reads files or stdin, outputs to stdout
- user/ls.c: lists directory entries via getdents syscall
- user/mkdir.c: creates directories via mkdir syscall
- user/rm.c: removes files (unlink) or directories (-r/-d rmdir)
- Makefile: build rules for all four + initrd packaging as /bin/*
- echo already exists as shell builtin
- cppcheck clean, 19/19 smoke tests pass

Makefile
user/cat.c [new file with mode: 0644]
user/ls.c [new file with mode: 0644]
user/mkdir.c [new file with mode: 0644]
user/rm.c [new file with mode: 0644]

index 88a8bf0bd25f541dba13dbbf42776685ba40afd3..4b0a13664642bdf627f8ca70417f3116049f4d47 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -47,6 +47,10 @@ ifeq ($(ARCH),x86)
     USER_ELF := user/init.elf
     ECHO_ELF := user/echo.elf
     SH_ELF := user/sh.elf
+    CAT_ELF := user/cat.elf
+    LS_ELF := user/ls.elf
+    MKDIR_ELF := user/mkdir.elf
+    RM_ELF := user/rm.elf
     INITRD_IMG := initrd.img
     MKINITRD := tools/mkinitrd
 endif
@@ -134,8 +138,20 @@ $(ECHO_ELF): user/echo.c user/linker.ld
 $(SH_ELF): user/sh.c user/linker.ld
        @i686-elf-gcc -m32 -I include -ffreestanding -fno-pie -no-pie -nostdlib -Wl,-T,user/linker.ld -o $(SH_ELF) user/sh.c user/errno.c
 
-$(INITRD_IMG): $(MKINITRD) $(USER_ELF) $(ECHO_ELF) $(SH_ELF)
-       @./$(MKINITRD) $(INITRD_IMG) $(USER_ELF):bin/init.elf $(ECHO_ELF):bin/echo.elf $(SH_ELF):bin/sh
+$(CAT_ELF): user/cat.c user/linker.ld
+       @i686-elf-gcc -m32 -I include -ffreestanding -fno-pie -no-pie -nostdlib -Wl,-T,user/linker.ld -o $(CAT_ELF) user/cat.c user/errno.c
+
+$(LS_ELF): user/ls.c user/linker.ld
+       @i686-elf-gcc -m32 -I include -ffreestanding -fno-pie -no-pie -nostdlib -Wl,-T,user/linker.ld -o $(LS_ELF) user/ls.c user/errno.c
+
+$(MKDIR_ELF): user/mkdir.c user/linker.ld
+       @i686-elf-gcc -m32 -I include -ffreestanding -fno-pie -no-pie -nostdlib -Wl,-T,user/linker.ld -o $(MKDIR_ELF) user/mkdir.c user/errno.c
+
+$(RM_ELF): user/rm.c user/linker.ld
+       @i686-elf-gcc -m32 -I include -ffreestanding -fno-pie -no-pie -nostdlib -Wl,-T,user/linker.ld -o $(RM_ELF) user/rm.c user/errno.c
+
+$(INITRD_IMG): $(MKINITRD) $(USER_ELF) $(ECHO_ELF) $(SH_ELF) $(CAT_ELF) $(LS_ELF) $(MKDIR_ELF) $(RM_ELF)
+       @./$(MKINITRD) $(INITRD_IMG) $(USER_ELF):bin/init.elf $(ECHO_ELF):bin/echo.elf $(SH_ELF):bin/sh $(CAT_ELF):bin/cat $(LS_ELF):bin/ls $(MKDIR_ELF):bin/mkdir $(RM_ELF):bin/rm
 
 run: iso
        @rm -f serial.log qemu.log
diff --git a/user/cat.c b/user/cat.c
new file mode 100644 (file)
index 0000000..d234951
--- /dev/null
@@ -0,0 +1,100 @@
+/* AdrOS cat utility */
+#include <stdint.h>
+#include "user_errno.h"
+
+enum {
+    SYSCALL_WRITE = 1,
+    SYSCALL_EXIT  = 2,
+    SYSCALL_OPEN  = 4,
+    SYSCALL_READ  = 5,
+    SYSCALL_CLOSE = 6,
+};
+
+static int sys_write(int fd, const void* buf, uint32_t len) {
+    int ret;
+    __asm__ volatile("int $0x80" : "=a"(ret)
+        : "a"(SYSCALL_WRITE), "b"(fd), "c"(buf), "d"(len) : "memory");
+    return __syscall_fix(ret);
+}
+
+static int sys_read(int fd, void* buf, uint32_t len) {
+    int ret;
+    __asm__ volatile("int $0x80" : "=a"(ret)
+        : "a"(SYSCALL_READ), "b"(fd), "c"(buf), "d"(len) : "memory");
+    return __syscall_fix(ret);
+}
+
+static int sys_open(const char* path, int flags) {
+    int ret;
+    __asm__ volatile("int $0x80" : "=a"(ret)
+        : "a"(SYSCALL_OPEN), "b"(path), "c"(flags) : "memory");
+    return __syscall_fix(ret);
+}
+
+static int sys_close(int fd) {
+    int ret;
+    __asm__ volatile("int $0x80" : "=a"(ret)
+        : "a"(SYSCALL_CLOSE), "b"(fd) : "memory");
+    return __syscall_fix(ret);
+}
+
+static __attribute__((noreturn)) void sys_exit(int code) {
+    __asm__ volatile("int $0x80" : : "a"(SYSCALL_EXIT), "b"(code) : "memory");
+    for (;;) __asm__ volatile("hlt");
+}
+
+static uint32_t slen(const char* s) {
+    uint32_t n = 0;
+    while (s && s[n]) n++;
+    return n;
+}
+
+static void wr(int fd, const char* s) {
+    (void)sys_write(fd, s, slen(s));
+}
+
+static void cat_fd(int fd) {
+    char buf[256];
+    int r;
+    while ((r = sys_read(fd, buf, sizeof(buf))) > 0) {
+        sys_write(1, buf, (uint32_t)r);
+    }
+}
+
+static void cat_main(uint32_t* sp0) {
+    uint32_t argc = sp0 ? sp0[0] : 0;
+    const char* const* argv = (const char* const*)(sp0 + 1);
+
+    if (argc <= 1) {
+        cat_fd(0);
+        sys_exit(0);
+    }
+
+    int rc = 0;
+    for (uint32_t i = 1; i < argc; i++) {
+        int fd = sys_open(argv[i], 0);
+        if (fd < 0) {
+            wr(2, "cat: ");
+            wr(2, argv[i]);
+            wr(2, ": No such file\n");
+            rc = 1;
+            continue;
+        }
+        cat_fd(fd);
+        sys_close(fd);
+    }
+    sys_exit(rc);
+}
+
+__attribute__((naked)) void _start(void) {
+    __asm__ volatile(
+        "mov %esp, %eax\n"
+        "push %eax\n"
+        "call cat_main\n"
+        "add $4, %esp\n"
+        "mov $0, %ebx\n"
+        "mov $2, %eax\n"
+        "int $0x80\n"
+        "hlt\n"
+    );
+}
diff --git a/user/ls.c b/user/ls.c
new file mode 100644 (file)
index 0000000..7daca3b
--- /dev/null
+++ b/user/ls.c
@@ -0,0 +1,124 @@
+/* AdrOS ls utility */
+#include <stdint.h>
+#include "user_errno.h"
+
+enum {
+    SYSCALL_WRITE    = 1,
+    SYSCALL_EXIT     = 2,
+    SYSCALL_OPEN     = 4,
+    SYSCALL_CLOSE    = 6,
+    SYSCALL_GETDENTS = 30,
+};
+
+static int sys_write(int fd, const void* buf, uint32_t len) {
+    int ret;
+    __asm__ volatile("int $0x80" : "=a"(ret)
+        : "a"(SYSCALL_WRITE), "b"(fd), "c"(buf), "d"(len) : "memory");
+    return __syscall_fix(ret);
+}
+
+static int sys_open(const char* path, int flags) {
+    int ret;
+    __asm__ volatile("int $0x80" : "=a"(ret)
+        : "a"(SYSCALL_OPEN), "b"(path), "c"(flags) : "memory");
+    return __syscall_fix(ret);
+}
+
+static int sys_close(int fd) {
+    int ret;
+    __asm__ volatile("int $0x80" : "=a"(ret)
+        : "a"(SYSCALL_CLOSE), "b"(fd) : "memory");
+    return __syscall_fix(ret);
+}
+
+static int sys_getdents(int fd, void* buf, uint32_t len) {
+    int ret;
+    __asm__ volatile("int $0x80" : "=a"(ret)
+        : "a"(SYSCALL_GETDENTS), "b"(fd), "c"(buf), "d"(len) : "memory");
+    return __syscall_fix(ret);
+}
+
+static __attribute__((noreturn)) void sys_exit(int code) {
+    __asm__ volatile("int $0x80" : : "a"(SYSCALL_EXIT), "b"(code) : "memory");
+    for (;;) __asm__ volatile("hlt");
+}
+
+static uint32_t slen(const char* s) {
+    uint32_t n = 0;
+    while (s && s[n]) n++;
+    return n;
+}
+
+static void wr(int fd, const char* s) {
+    (void)sys_write(fd, s, slen(s));
+}
+
+struct dirent {
+    uint32_t d_ino;
+    uint16_t d_reclen;
+    uint8_t  d_type;
+    char     d_name[24];
+};
+
+static void ls_dir(const char* path) {
+    int fd = sys_open(path, 0);
+    if (fd < 0) {
+        wr(2, "ls: cannot open ");
+        wr(2, path);
+        wr(2, "\n");
+        return;
+    }
+
+    char buf[512];
+    int rc = sys_getdents(fd, buf, sizeof(buf));
+    if (rc > 0) {
+        uint32_t off = 0;
+        while (off + sizeof(struct dirent) <= (uint32_t)rc) {
+            struct dirent* d = (struct dirent*)(buf + off);
+            if (d->d_reclen == 0) break;
+            /* skip . and .. */
+            if (d->d_name[0] == '.' &&
+                (d->d_name[1] == 0 ||
+                 (d->d_name[1] == '.' && d->d_name[2] == 0))) {
+                off += d->d_reclen;
+                continue;
+            }
+            wr(1, d->d_name);
+            wr(1, "\n");
+            off += d->d_reclen;
+        }
+    }
+
+    sys_close(fd);
+}
+
+static void ls_main(uint32_t* sp0) {
+    uint32_t argc = sp0 ? sp0[0] : 0;
+    const char* const* argv = (const char* const*)(sp0 + 1);
+
+    if (argc <= 1) {
+        ls_dir(".");
+    } else {
+        for (uint32_t i = 1; i < argc; i++) {
+            if (argc > 2) {
+                wr(1, argv[i]);
+                wr(1, ":\n");
+            }
+            ls_dir(argv[i]);
+        }
+    }
+    sys_exit(0);
+}
+
+__attribute__((naked)) void _start(void) {
+    __asm__ volatile(
+        "mov %esp, %eax\n"
+        "push %eax\n"
+        "call ls_main\n"
+        "add $4, %esp\n"
+        "mov $0, %ebx\n"
+        "mov $2, %eax\n"
+        "int $0x80\n"
+        "hlt\n"
+    );
+}
diff --git a/user/mkdir.c b/user/mkdir.c
new file mode 100644 (file)
index 0000000..ee171ae
--- /dev/null
@@ -0,0 +1,72 @@
+/* AdrOS mkdir utility */
+#include <stdint.h>
+#include "user_errno.h"
+
+enum {
+    SYSCALL_WRITE = 1,
+    SYSCALL_EXIT  = 2,
+    SYSCALL_MKDIR = 28,
+};
+
+static int sys_write(int fd, const void* buf, uint32_t len) {
+    int ret;
+    __asm__ volatile("int $0x80" : "=a"(ret)
+        : "a"(SYSCALL_WRITE), "b"(fd), "c"(buf), "d"(len) : "memory");
+    return __syscall_fix(ret);
+}
+
+static int sys_mkdir(const char* path) {
+    int ret;
+    __asm__ volatile("int $0x80" : "=a"(ret)
+        : "a"(SYSCALL_MKDIR), "b"(path) : "memory");
+    return __syscall_fix(ret);
+}
+
+static __attribute__((noreturn)) void sys_exit(int code) {
+    __asm__ volatile("int $0x80" : : "a"(SYSCALL_EXIT), "b"(code) : "memory");
+    for (;;) __asm__ volatile("hlt");
+}
+
+static uint32_t slen(const char* s) {
+    uint32_t n = 0;
+    while (s && s[n]) n++;
+    return n;
+}
+
+static void wr(int fd, const char* s) {
+    (void)sys_write(fd, s, slen(s));
+}
+
+static void mkdir_main(uint32_t* sp0) {
+    uint32_t argc = sp0 ? sp0[0] : 0;
+    const char* const* argv = (const char* const*)(sp0 + 1);
+
+    if (argc <= 1) {
+        wr(2, "mkdir: missing operand\n");
+        sys_exit(1);
+    }
+
+    int rc = 0;
+    for (uint32_t i = 1; i < argc; i++) {
+        if (sys_mkdir(argv[i]) < 0) {
+            wr(2, "mkdir: cannot create '");
+            wr(2, argv[i]);
+            wr(2, "'\n");
+            rc = 1;
+        }
+    }
+    sys_exit(rc);
+}
+
+__attribute__((naked)) void _start(void) {
+    __asm__ volatile(
+        "mov %esp, %eax\n"
+        "push %eax\n"
+        "call mkdir_main\n"
+        "add $4, %esp\n"
+        "mov $0, %ebx\n"
+        "mov $2, %eax\n"
+        "int $0x80\n"
+        "hlt\n"
+    );
+}
diff --git a/user/rm.c b/user/rm.c
new file mode 100644 (file)
index 0000000..1f17524
--- /dev/null
+++ b/user/rm.c
@@ -0,0 +1,98 @@
+/* AdrOS rm utility */
+#include <stdint.h>
+#include "user_errno.h"
+
+enum {
+    SYSCALL_WRITE  = 1,
+    SYSCALL_EXIT   = 2,
+    SYSCALL_UNLINK = 29,
+    SYSCALL_RMDIR  = 40,
+};
+
+static int sys_write(int fd, const void* buf, uint32_t len) {
+    int ret;
+    __asm__ volatile("int $0x80" : "=a"(ret)
+        : "a"(SYSCALL_WRITE), "b"(fd), "c"(buf), "d"(len) : "memory");
+    return __syscall_fix(ret);
+}
+
+static int sys_unlink(const char* path) {
+    int ret;
+    __asm__ volatile("int $0x80" : "=a"(ret)
+        : "a"(SYSCALL_UNLINK), "b"(path) : "memory");
+    return __syscall_fix(ret);
+}
+
+static int sys_rmdir(const char* path) {
+    int ret;
+    __asm__ volatile("int $0x80" : "=a"(ret)
+        : "a"(SYSCALL_RMDIR), "b"(path) : "memory");
+    return __syscall_fix(ret);
+}
+
+static __attribute__((noreturn)) void sys_exit(int code) {
+    __asm__ volatile("int $0x80" : : "a"(SYSCALL_EXIT), "b"(code) : "memory");
+    for (;;) __asm__ volatile("hlt");
+}
+
+static uint32_t slen(const char* s) {
+    uint32_t n = 0;
+    while (s && s[n]) n++;
+    return n;
+}
+
+static void wr(int fd, const char* s) {
+    (void)sys_write(fd, s, slen(s));
+}
+
+static int scmp(const char* a, const char* b) {
+    while (*a && *b && *a == *b) { a++; b++; }
+    return (unsigned char)*a - (unsigned char)*b;
+}
+
+static void rm_main(uint32_t* sp0) {
+    uint32_t argc = sp0 ? sp0[0] : 0;
+    const char* const* argv = (const char* const*)(sp0 + 1);
+
+    if (argc <= 1) {
+        wr(2, "rm: missing operand\n");
+        sys_exit(1);
+    }
+
+    int rflag = 0;
+    int rc = 0;
+    uint32_t start = 1;
+
+    if (argv[1] && (scmp(argv[1], "-r") == 0 || scmp(argv[1], "-rf") == 0 ||
+                    scmp(argv[1], "-d") == 0)) {
+        rflag = 1;
+        start = 2;
+    }
+
+    for (uint32_t i = start; i < argc; i++) {
+        int r = sys_unlink(argv[i]);
+        if (r < 0 && rflag) {
+            r = sys_rmdir(argv[i]);
+        }
+        if (r < 0) {
+            wr(2, "rm: cannot remove '");
+            wr(2, argv[i]);
+            wr(2, "'\n");
+            rc = 1;
+        }
+    }
+    sys_exit(rc);
+}
+
+__attribute__((naked)) void _start(void) {
+    __asm__ volatile(
+        "mov %esp, %eax\n"
+        "push %eax\n"
+        "call rm_main\n"
+        "add $4, %esp\n"
+        "mov $0, %ebx\n"
+        "mov $2, %eax\n"
+        "int $0x80\n"
+        "hlt\n"
+    );
+}