From ba281ea3f32a01f07928a0189a889a16cf4a889e Mon Sep 17 00:00:00 2001 From: Lluciocc <114759545+Lluciocc@users.noreply.github.com> Date: Mon, 11 May 2026 19:32:26 +0200 Subject: [PATCH 01/16] Adding hexdump and ps.c --- src/userland/cli/hexdump.c | 111 ++++++++++++++ src/userland/cli/ps.c | 299 +++++++++++++++++++++++++++++++++++++ 2 files changed, 410 insertions(+) create mode 100644 src/userland/cli/hexdump.c create mode 100644 src/userland/cli/ps.c diff --git a/src/userland/cli/hexdump.c b/src/userland/cli/hexdump.c new file mode 100644 index 0000000..f722d63 --- /dev/null +++ b/src/userland/cli/hexdump.c @@ -0,0 +1,111 @@ +// Copyright (c) 2023-2026 Chris (boreddevnl) +// This software is released under the GNU General Public License v3.0. See LICENSE file for details. +// This header needs to maintain in any file it is present in, as per the GPL license terms. +// BOREDOS_APP_DESC: Display file contents in hexadecimal. + +#include "syscall.h" +#include "stdlib.h" +#include "string.h" +#include "stdio.h" + +#define BYTES_PER_LINE 16 + +static int sc_strcmp(const char *a, const char *b) { + while (*a && *a == *b) { + a++; + b++; + } + + return (unsigned char)*a - (unsigned char)*b; +} + +static void print_usage(void) { + printf("Usage: hexdump \n"); + printf("\n"); + printf("Display file contents in hexadecimal.\n"); +} + +static void print_hex_byte(unsigned char b) { + const char *hex = "0123456789ABCDEF"; + + putchar(hex[(b >> 4) & 0xF]); + putchar(hex[b & 0xF]); +} + +static void print_hex32(unsigned int v) { + const char *hex = "0123456789ABCDEF"; + + for (int i = 7; i >= 0; i--) { + putchar(hex[(v >> (i * 4)) & 0xF]); + } +} + +int main(int argc, char **argv) { + int fd; + int offset = 0; + + unsigned char buf[BYTES_PER_LINE]; + + if (argc < 2) { + print_usage(); + return 1; + } + + if (sc_strcmp(argv[1], "-h") == 0 || + sc_strcmp(argv[1], "--help") == 0) { + print_usage(); + return 0; + } + + fd = sys_open(argv[1], "r"); + + if (fd < 0) { + printf("hexdump: cannot open '%s'\n", argv[1]); + return 1; + } + + while (1) { + int bytes = sys_read(fd, buf, BYTES_PER_LINE); + + if (bytes <= 0) + break; + + // Offset + print_hex32(offset); + printf(" "); + + // Hex bytes + for (int i = 0; i < BYTES_PER_LINE; i++) { + if (i < bytes) { + print_hex_byte(buf[i]); + } else { + printf(" "); + } + + printf(" "); + + if (i == 7) + printf(" "); + } + + printf(" |"); + + // ASCII preview + for (int i = 0; i < bytes; i++) { + unsigned char c = buf[i]; + + if (c >= 32 && c <= 126) + putchar(c); + else + putchar('.'); + } + + printf("|\n"); + + offset += bytes; + } + + sys_close(fd); + + return 0; +} \ No newline at end of file diff --git a/src/userland/cli/ps.c b/src/userland/cli/ps.c new file mode 100644 index 0000000..f34554d --- /dev/null +++ b/src/userland/cli/ps.c @@ -0,0 +1,299 @@ +// Copyright (c) 2023-2026 Chris (boreddevnl) +// This software is released under the GNU General Public License v3.0. See LICENSE file for details. +// This header needs to maintain in any file it is present in, as per the GPL license terms. +// BOREDOS_APP_DESC: List running processes. + +#include "../libc/syscall.h" +#include "../libc/stdlib.h" +#include "../libc/string.h" +#include "../libc/stdio.h" + +#define MAX_PROC_ENTRIES 64 + +static int sc_strcmp(const char *a, const char *b) { + while (*a && *a == *b) { + a++; + b++; + } + return (unsigned char)*a - (unsigned char)*b; +} + +static int is_numeric(const char *s) { + int i = 0; + + if (!s || !s[0]) + return 0; + + while (s[i]) { + if (s[i] < '0' || s[i] > '9') + return 0; + i++; + } + + return 1; +} + +static void print_spaces(int count) { + for (int i = 0; i < count; i++) + printf(" "); +} + +static void print_padded(const char *s, int width) { + int len; + + if (!s) + s = ""; + + printf("%s", s); + + len = (int)strlen(s); + + if (len < width) + print_spaces(width - len); + else + printf(" "); +} + +static int find_value(const char *buf, const char *key) { + char *p = (char*)buf; + int key_len = strlen(key); + + while (*p) { + if (memcmp(p, key, key_len) == 0 && p[key_len] == ':') { + p += key_len + 1; + + while (*p == ' ' || *p == '\t') + p++; + + return atoi(p); + } + + while (*p && *p != '\n') + p++; + + if (*p == '\n') + p++; + } + + return 0; +} + +static void find_string(const char *buf, + const char *key, + char *out, + int max_len) { + char *p = (char*)buf; + int key_len = strlen(key); + + out[0] = 0; + + while (*p) { + if (memcmp(p, key, key_len) == 0 && p[key_len] == ':') { + int i = 0; + + p += key_len + 1; + + while (*p == ' ' || *p == '\t') + p++; + + while (*p && + *p != '\n' && + *p != '\r' && + i < max_len - 1) { + out[i++] = *p++; + } + + out[i] = 0; + return; + } + + while (*p && *p != '\n') + p++; + + if (*p == '\n') + p++; + } +} + +static int read_file_to_buf(const char *path, + char *buf, + int max_len) { + int fd; + int bytes; + + fd = sys_open(path, "r"); + + if (fd < 0) + return -1; + + bytes = sys_read(fd, buf, max_len - 1); + + sys_close(fd); + + if (bytes < 0) + return -1; + + buf[bytes] = 0; + + return bytes; +} + +static void format_mem(int kb, char *out) { + char tmp[32]; + + if (kb < 1024) { + itoa(kb, tmp); + + strcpy(out, tmp); + strcat(out, " KB"); + } else { + int mb = kb / 1024; + int frac = ((kb % 1024) * 10) / 1024; + + itoa(mb, tmp); + + strcpy(out, tmp); + strcat(out, "."); + + itoa(frac, tmp); + strcat(out, tmp); + strcat(out, " MiB"); + } +} + +static void print_usage(void) { + printf("Usage: ps [options]\n"); + printf("\n"); + printf("Options:\n"); + printf(" -a Show all processes\n"); + printf(" -i Include idle tasks\n"); + printf(" -m Show memory usage\n"); + printf(" -t Show scheduler ticks\n"); + printf(" -p PID Show only one process\n"); + printf(" -h Show this help\n"); +} + +static void print_header(int show_mem, + int show_ticks, + int show_idle) { + print_padded("PID", 8); + print_padded("NAME", 22); + + if (show_mem) + print_padded("MEMORY", 14); + + if (show_ticks) + print_padded("TICKS", 12); + + if (show_idle) + print_padded("IDLE", 8); + + printf("\n"); +} + +int main(int argc, char **argv) { + int show_mem = 0; + int show_ticks = 0; + int include_idle = 0; + int show_idle_col = 0; + int filter_pid = -1; + + FAT32_FileInfo entries[MAX_PROC_ENTRIES]; + + for (int i = 1; i < argc; i++) { + if (sc_strcmp(argv[i], "-m") == 0) { + show_mem = 1; + } else if (sc_strcmp(argv[i], "-t") == 0) { + show_ticks = 1; + } else if (sc_strcmp(argv[i], "-a") == 0) { + include_idle = 1; + show_idle_col = 1; + } else if (sc_strcmp(argv[i], "-i") == 0) { + include_idle = 1; + show_idle_col = 1; + } else if (sc_strcmp(argv[i], "-p") == 0 && i + 1 < argc) { + filter_pid = atoi(argv[++i]); + } else if (sc_strcmp(argv[i], "-h") == 0) { + print_usage(); + return 0; + } else { + print_usage(); + return 1; + } + } + + int count = sys_list("/proc", entries, MAX_PROC_ENTRIES); + + if (count < 0) { + printf("ps: failed to read /proc\n"); + return 1; + } + + print_header(show_mem, show_ticks, show_idle_col); + + for (int i = 0; i < count; i++) { + char path[96]; + char buf[512]; + char name[64]; + char mem_str[32]; + char tmp[32]; + + int pid; + int memory_kb; + int ticks; + int idle; + + if (!entries[i].is_directory) + continue; + + if (!is_numeric(entries[i].name)) + continue; + + pid = atoi(entries[i].name); + + if (filter_pid >= 0 && pid != filter_pid) + continue; + + strcpy(path, "/proc/"); + strcat(path, entries[i].name); + strcat(path, "/status"); + + if (read_file_to_buf(path, buf, sizeof(buf)) <= 0) + continue; + + find_string(buf, "Name", name, sizeof(name)); + + memory_kb = find_value(buf, "Memory"); + ticks = find_value(buf, "Ticks"); + idle = find_value(buf, "Idle"); + + if (idle && !include_idle) + continue; + + itoa(pid, tmp); + print_padded(tmp, 8); + + if (!name[0]) + strcpy(name, "Unknown"); + + print_padded(name, 22); + + if (show_mem) { + format_mem(memory_kb, mem_str); + print_padded(mem_str, 14); + } + + if (show_ticks) { + itoa(ticks, tmp); + print_padded(tmp, 12); + } + + if (show_idle_col) { + print_padded(idle ? "yes" : "no", 8); + } + + printf("\n"); + } + + return 0; +} \ No newline at end of file From f94384e572f0f83e63a1ae291d24f55f51394a45 Mon Sep 17 00:00:00 2001 From: Lluciocc <114759545+Lluciocc@users.noreply.github.com> Date: Mon, 11 May 2026 19:36:27 +0200 Subject: [PATCH 02/16] Add hexdump and ps command descriptions to help --- src/userland/cli/help.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/userland/cli/help.c b/src/userland/cli/help.c index d5f7903..490a1eb 100644 --- a/src/userland/cli/help.c +++ b/src/userland/cli/help.c @@ -25,6 +25,8 @@ int main(int argc, char **argv) { printf("date - Print current date and time\n"); printf("uptime - Print system uptime\n"); printf("meminfo - Print memory information\n"); + printf("hexdump - Display file contents in hexadecimal.\n"); + printf("ps [options] - List running processes\n") printf("lsblk - List block devices and partitions\n"); printf("cowsay [msg] - Fun cow says something\n"); printf("beep - Make a beep sound\n"); From fe1ba182d95676423beef510d129ed655f84b82b Mon Sep 17 00:00:00 2001 From: Lluciocc <114759545+Lluciocc@users.noreply.github.com> Date: Mon, 11 May 2026 20:25:35 +0200 Subject: [PATCH 03/16] Update with missing ; --- src/userland/cli/help.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/userland/cli/help.c b/src/userland/cli/help.c index 490a1eb..20ff62a 100644 --- a/src/userland/cli/help.c +++ b/src/userland/cli/help.c @@ -26,7 +26,7 @@ int main(int argc, char **argv) { printf("uptime - Print system uptime\n"); printf("meminfo - Print memory information\n"); printf("hexdump - Display file contents in hexadecimal.\n"); - printf("ps [options] - List running processes\n") + printf("ps [options] - List running processes\n"); printf("lsblk - List block devices and partitions\n"); printf("cowsay [msg] - Fun cow says something\n"); printf("beep - Make a beep sound\n"); From 70ab1837c2cd3db216c3f5ffb3d02ecf054743cc Mon Sep 17 00:00:00 2001 From: Lluciocc <114759545+Lluciocc@users.noreply.github.com> Date: Tue, 12 May 2026 09:04:27 +0200 Subject: [PATCH 04/16] Add ptime --- src/userland/cli/ptime.c | 140 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 140 insertions(+) create mode 100644 src/userland/cli/ptime.c diff --git a/src/userland/cli/ptime.c b/src/userland/cli/ptime.c new file mode 100644 index 0000000..10a411e --- /dev/null +++ b/src/userland/cli/ptime.c @@ -0,0 +1,140 @@ +// Copyright (c) 2023-2026 Chris (boreddevnl) +// This software is released under the GNU General Public License v3.0. See LICENSE file for details. +// This header needs to maintain in any file it is present in, as per the GPL license terms. + +#include "../libc/stdlib.h" + +#define CMDLINE_MAX 512 + +static int sc_strcmp(const char *a, const char *b) { + while (*a && *a == *b) { + a++; + b++; + } + + return (unsigned char)*a - (unsigned char)*b; +} + +static int has_slash(const char *s) { + while (s && *s) { + if (*s == '/') + return 1; + s++; + } + + return 0; +} + +static int ends_with_elf(const char *s) { + int len; + + if (!s) + return 0; + + len = strlen(s); + + if (len < 4) + return 0; + + return sc_strcmp(s + len - 4, ".elf") == 0; +} + +static void print_usage(void) { + printf("Usage: ptime [args...]\n"); + printf("\n"); + printf("Examples:\n"); + printf(" ptime ls\n"); + printf(" ptime hexdump file.txt\n"); + printf(" ptime /bin/hexdump.elf file.txt\n"); +} + +// Read the system uptime in milliseconds by reading /proc/uptime and parsing the first number (seconds). +static unsigned long long read_uptime_ms(void) { + char buf[64]; + int fd; + int bytes; + int seconds; + + fd = sys_open("/proc/uptime", "r"); + + if (fd < 0) + return 0; + + bytes = sys_read(fd, buf, sizeof(buf) - 1); + + sys_close(fd); + + if (bytes <= 0) + return 0; + + buf[bytes] = 0; + + seconds = atoi(buf); + + return (unsigned long long)seconds * 1000ULL; +} + +// Build the command line for execution +// If the first argument contains a slash, use it as is. Otherwise, prepend "/bin/" and append ".elf" if it doesn't already end with ".elf". +static void build_command_line(int argc, char **argv, char *out) { + int i; + + out[0] = 0; + + if (has_slash(argv[1])) { + strcat(out, argv[1]); + } else { + strcat(out, "/bin/"); + strcat(out, argv[1]); + + if (!ends_with_elf(argv[1])) { + strcat(out, ".elf"); + } + } + + for (i = 2; i < argc; i++) { + strcat(out, " "); + strcat(out, argv[i]); + } +} + +int main(int argc, char **argv) { + char cmdline[CMDLINE_MAX]; + + unsigned long long start; + unsigned long long end; + unsigned long long elapsed; + + int ret; + + if (argc < 2) { + print_usage(); + return 1; + } + + if (sc_strcmp(argv[1], "-h") == 0 || + sc_strcmp(argv[1], "--help") == 0) { + print_usage(); + return 0; + } + + build_command_line(argc, argv, cmdline); + + start = read_uptime_ms(); + + ret = system(cmdline); + + end = read_uptime_ms(); + + if (end >= start) + elapsed = end - start; + else + elapsed = 0; + + printf("\n"); + printf("Command: %s\n", cmdline); + printf("Exit code: %d\n", ret); + printf("Elapsed: %llu ms\n", elapsed); + + return ret; +} From 11593b1b2384ae1199dc1fbe743ddc5499d3ba7d Mon Sep 17 00:00:00 2001 From: Lluciocc <114759545+Lluciocc@users.noreply.github.com> Date: Tue, 12 May 2026 09:06:05 +0200 Subject: [PATCH 05/16] Add ptime command help message --- src/userland/cli/help.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/userland/cli/help.c b/src/userland/cli/help.c index 20ff62a..83ee46b 100644 --- a/src/userland/cli/help.c +++ b/src/userland/cli/help.c @@ -38,6 +38,7 @@ int main(int argc, char **argv) { printf("clear - Clear the screen\n"); printf("exit - Exit the terminal\n"); printf("net - Network tools\n"); + printf("ptime - Measure command execution time\n"); printf("\nHint: Use Ctrl+C to force quit any running application.\n"); return 0; } From 0075493fbaa5e22f92a6bbb88a74ed5a09527c40 Mon Sep 17 00:00:00 2001 From: Lluciocc <114759545+Lluciocc@users.noreply.github.com> Date: Tue, 12 May 2026 18:31:25 +0200 Subject: [PATCH 06/16] Rename object files in Makefile to include 'libc_' prefix --- src/userland/Makefile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/userland/Makefile b/src/userland/Makefile index 5b80484..a0d6906 100644 --- a/src/userland/Makefile +++ b/src/userland/Makefile @@ -20,7 +20,7 @@ $(if $(filter doom,$1),$(APP_METADATA_SOURCE_DOOM),$(if $(filter lua,$1),$(APP_M endef LIBC_SOURCES = $(wildcard libc/*.c) -LIBC_OBJS = $(patsubst libc/%.c, $(BIN_DIR)/%.o, $(LIBC_SOURCES)) $(BIN_DIR)/crt0.o $(BIN_DIR)/libwidget.o $(BIN_DIR)/stb_image.o +LIBC_OBJS = $(patsubst libc/%.c, $(BIN_DIR)/libc_%.o, $(LIBC_SOURCES)) $(BIN_DIR)/crt0.o $(BIN_DIR)/libwidget.o $(BIN_DIR)/stb_image.o VPATH = cli gui sys games libc net cli/third_party vpath %.c cli gui sys games libc net cli/third_party @@ -46,7 +46,7 @@ $(BIN_DIR): $(BIN_DIR)/crt0.o: crt0.asm $(AS) -f elf64 $< -o $@ -$(BIN_DIR)/%.o: libc/%.c | $(BIN_DIR) +$(BIN_DIR)/libc_%.o: libc/%.c | $(BIN_DIR) $(CC) $(CFLAGS) -c $< -o $@ $(BIN_DIR)/libwidget.o: ../wm/libwidget.c | $(BIN_DIR) From c275da6145d4d8e1a7e7ee8742663115c9dc0230 Mon Sep 17 00:00:00 2001 From: Lluciocc <114759545+Lluciocc@users.noreply.github.com> Date: Tue, 12 May 2026 18:32:36 +0200 Subject: [PATCH 07/16] Rename ptime to time --- src/userland/cli/{ptime.c => time.c} | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) rename src/userland/cli/{ptime.c => time.c} (94%) diff --git a/src/userland/cli/ptime.c b/src/userland/cli/time.c similarity index 94% rename from src/userland/cli/ptime.c rename to src/userland/cli/time.c index 10a411e..9a8ffaa 100644 --- a/src/userland/cli/ptime.c +++ b/src/userland/cli/time.c @@ -40,12 +40,12 @@ static int ends_with_elf(const char *s) { } static void print_usage(void) { - printf("Usage: ptime [args...]\n"); + printf("Usage: time [args...]\n"); printf("\n"); printf("Examples:\n"); - printf(" ptime ls\n"); - printf(" ptime hexdump file.txt\n"); - printf(" ptime /bin/hexdump.elf file.txt\n"); + printf(" time ls\n"); + printf(" time hexdump file.txt\n"); + printf(" time /bin/hexdump.elf file.txt\n"); } // Read the system uptime in milliseconds by reading /proc/uptime and parsing the first number (seconds). From 078ad437a5d38939e26633a09334b3ac273ad00b Mon Sep 17 00:00:00 2001 From: Lluciocc <114759545+Lluciocc@users.noreply.github.com> Date: Tue, 12 May 2026 18:33:08 +0200 Subject: [PATCH 08/16] Rename 'ptime' command to 'time' in help text --- src/userland/cli/help.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/userland/cli/help.c b/src/userland/cli/help.c index 83ee46b..8f651e3 100644 --- a/src/userland/cli/help.c +++ b/src/userland/cli/help.c @@ -38,7 +38,7 @@ int main(int argc, char **argv) { printf("clear - Clear the screen\n"); printf("exit - Exit the terminal\n"); printf("net - Network tools\n"); - printf("ptime - Measure command execution time\n"); + printf("time - Measure command execution time\n"); printf("\nHint: Use Ctrl+C to force quit any running application.\n"); return 0; } From 2580700ff928e2bceb68c88848530dae882c66d1 Mon Sep 17 00:00:00 2001 From: Lluciocc <114759545+Lluciocc@users.noreply.github.com> Date: Tue, 12 May 2026 18:40:27 +0200 Subject: [PATCH 09/16] Update time.c --- src/userland/cli/time.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/userland/cli/time.c b/src/userland/cli/time.c index 9a8ffaa..96776a1 100644 --- a/src/userland/cli/time.c +++ b/src/userland/cli/time.c @@ -2,7 +2,8 @@ // This software is released under the GNU General Public License v3.0. See LICENSE file for details. // This header needs to maintain in any file it is present in, as per the GPL license terms. -#include "../libc/stdlib.h" +#include "stdlib.h" +#include "syscall.h" #define CMDLINE_MAX 512 From 69d5f8fefffa1afd529649265b22d497b330d00c Mon Sep 17 00:00:00 2001 From: Lluciocc Date: Wed, 13 May 2026 08:11:00 +0200 Subject: [PATCH 10/16] Update header --- src/userland/cli/time.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/userland/cli/time.c b/src/userland/cli/time.c index 96776a1..c132fc9 100644 --- a/src/userland/cli/time.c +++ b/src/userland/cli/time.c @@ -1,4 +1,4 @@ -// Copyright (c) 2023-2026 Chris (boreddevnl) +// Copyright (c) 2026 Lluciocc (https://github.com/lluciocc) // This software is released under the GNU General Public License v3.0. See LICENSE file for details. // This header needs to maintain in any file it is present in, as per the GPL license terms. From 29e1b362ff5f74cacdcf39cf61e92ffb56b7ab98 Mon Sep 17 00:00:00 2001 From: Lluciocc Date: Wed, 13 May 2026 09:17:08 +0200 Subject: [PATCH 11/16] replacing with strcmp && Adding comments && adding helper when the command don't exist --- src/userland/cli/time.c | 27 +++++++++++++++------------ 1 file changed, 15 insertions(+), 12 deletions(-) diff --git a/src/userland/cli/time.c b/src/userland/cli/time.c index c132fc9..cfd13db 100644 --- a/src/userland/cli/time.c +++ b/src/userland/cli/time.c @@ -5,17 +5,12 @@ #include "stdlib.h" #include "syscall.h" +// CMDLINE_MAX includes the trailing NUL, so at most 511 command-line bytes can +// be reconstructed here. Unchecked concatenation can overflow CMDLINE_MAX; all +// command-line construction in this file must go through the checked append +// helpers below. #define CMDLINE_MAX 512 -static int sc_strcmp(const char *a, const char *b) { - while (*a && *a == *b) { - a++; - b++; - } - - return (unsigned char)*a - (unsigned char)*b; -} - static int has_slash(const char *s) { while (s && *s) { if (*s == '/') @@ -37,7 +32,7 @@ static int ends_with_elf(const char *s) { if (len < 4) return 0; - return sc_strcmp(s + len - 4, ".elf") == 0; + return strcmp(s + len - 4, ".elf") == 0; } static void print_usage(void) { @@ -113,8 +108,8 @@ int main(int argc, char **argv) { return 1; } - if (sc_strcmp(argv[1], "-h") == 0 || - sc_strcmp(argv[1], "--help") == 0) { + if (strcmp(argv[1], "-h") == 0 || + strcmp(argv[1], "--help") == 0) { print_usage(); return 0; } @@ -135,7 +130,15 @@ int main(int argc, char **argv) { printf("\n"); printf("Command: %s\n", cmdline); printf("Exit code: %d\n", ret); + + if (ret == -1) { + printf("Command failed with non-zero exit code, not reporting time.\n"); + return ret; + } + printf("Elapsed: %llu ms\n", elapsed); + sys_system(SYSTEM_CMD_SLEEP, 1, 0, 0, 0); + return ret; } From fdd25b31cd8a8a884acd6e40f894f50c69faa2c0 Mon Sep 17 00:00:00 2001 From: Lluciocc Date: Thu, 14 May 2026 00:47:47 +0200 Subject: [PATCH 12/16] Feat: Implementing time command inside bsh.c --- src/userland/cli/bsh.c | 316 ++++++++++++++++++++++++++++++++-------- src/userland/cli/time.c | 144 ------------------ 2 files changed, 256 insertions(+), 204 deletions(-) delete mode 100644 src/userland/cli/time.c diff --git a/src/userland/cli/bsh.c b/src/userland/cli/bsh.c index 35b10a1..5f329df 100644 --- a/src/userland/cli/bsh.c +++ b/src/userland/cli/bsh.c @@ -4,6 +4,7 @@ #include #include #include +#include #include "utf-8.h" #define MAX_LINE 512 @@ -37,6 +38,8 @@ typedef struct { static bsh_config_t g_cfg; static char g_paths[MAX_PATHS][MAX_PATH_LEN]; static int g_path_count = 0; +static char g_resolved_command_path[256]; +static int g_resolve_status = -1; static char g_history[MAX_HISTORY][MAX_LINE]; static int g_history_count = 0; @@ -665,55 +668,138 @@ static bool is_elf_file(const char *path) { return hdr[0] == 0x7F && hdr[1] == 'E' && hdr[2] == 'L' && hdr[3] == 'F'; } -static int resolve_command(const char *cmd, char *out, int out_len) { - if (!cmd || !cmd[0]) return -1; - - bool has_slash = false; +static bool contains_slash(const char *cmd) { + if (!cmd) return false; for (int i = 0; cmd[i]; i++) { - if (cmd[i] == '/') { has_slash = true; break; } + if (cmd[i] == '/') return true; + } + return false; +} + +static const char *env_get_value(char *const envp[], const char *name) { + int name_len; + + if (!envp || !name || !name[0]) return NULL; + + name_len = (int)strlen(name); + + for (int i = 0; envp[i]; i++) { + if (starts_with(envp[i], name) && envp[i][name_len] == '=') { + return envp[i] + name_len + 1; + } } - if (has_slash) { - if (sys_exists(cmd)) { - if (!is_file_path(cmd)) return -2; - if (!is_elf_file(cmd)) return -3; - str_copy(out, cmd, out_len); - return 0; - } - if (!ends_with(cmd, ".elf")) { - char temp[256]; - str_copy(temp, cmd, sizeof(temp)); - str_append(temp, ".elf", sizeof(temp)); - if (sys_exists(temp)) { - if (!is_file_path(temp)) return -2; - if (!is_elf_file(temp)) return -3; - str_copy(out, temp, out_len); - return 0; + return NULL; +} + +static void build_path_candidate(char *out, int out_len, const char *dir, int dir_len, const char *cmd) { + int pos = 0; + + if (!out || out_len <= 0) return; + out[0] = 0; + if (!dir || !cmd) return; + + for (int i = 0; i < dir_len && dir[i] && pos < out_len - 1; i++) { + out[pos++] = dir[i]; + } + out[pos] = 0; + + if (pos > 0 && out[pos - 1] != '/' && pos < out_len - 1) { + out[pos++] = '/'; + out[pos] = 0; + } + + for (int i = 0; cmd[i] && pos < out_len - 1; i++) { + out[pos++] = cmd[i]; + } + out[pos] = 0; +} + +static int accept_command_candidate(const char *candidate) { + if (access(candidate, X_OK) != 0) return -1; + if (!is_file_path(candidate)) return -2; + if (!is_elf_file(candidate)) return -3; + + str_copy(g_resolved_command_path, candidate, sizeof(g_resolved_command_path)); + return 0; +} + +static char *resolve_in_path_string(const char *cmd, const char *path_str) { + int first_error = -1; + int start = 0; + int i = 0; + + if (!path_str || !path_str[0]) { + g_resolve_status = -1; + return NULL; + } + + while (1) { + if (path_str[i] == ':' || path_str[i] == 0) { + int len = i - start; + + if (len > 0) { + char candidate[256]; + int res; + + build_path_candidate(candidate, sizeof(candidate), path_str + start, len, cmd); + res = accept_command_candidate(candidate); + if (res == 0) { + g_resolve_status = 0; + return g_resolved_command_path; + } + if (res != -1 && first_error == -1) first_error = res; + + if (!ends_with(cmd, ".elf")) { + str_append(candidate, ".elf", sizeof(candidate)); + res = accept_command_candidate(candidate); + if (res == 0) { + g_resolve_status = 0; + return g_resolved_command_path; + } + if (res != -1 && first_error == -1) first_error = res; + } } + + start = i + 1; } - return -1; + + if (path_str[i] == 0) break; + i++; } - for (int i = 0; i < g_path_count; i++) { - char temp[256]; - temp[0] = 0; - str_copy(temp, g_paths[i], sizeof(temp)); - if (temp[0] && temp[strlen(temp) - 1] != '/') str_append(temp, "/", sizeof(temp)); - str_append(temp, cmd, sizeof(temp)); - if (sys_exists(temp) && is_file_path(temp) && is_elf_file(temp)) { - str_copy(out, temp, out_len); - return 0; - } - if (!ends_with(cmd, ".elf")) { - str_append(temp, ".elf", sizeof(temp)); - if (sys_exists(temp) && is_file_path(temp) && is_elf_file(temp)) { - str_copy(out, temp, out_len); - return 0; - } - } + g_resolve_status = first_error; + return NULL; +} + +static char *resolve_command_path(const char *cmd, char *const envp[]) { + const char *path; + int res; + + g_resolved_command_path[0] = 0; + g_resolve_status = -1; + + if (!cmd || !cmd[0]) return NULL; + + if (contains_slash(cmd)) { + res = accept_command_candidate(cmd); + g_resolve_status = res; + return (res == 0) ? g_resolved_command_path : NULL; } - return -1; + path = env_get_value(envp, "PATH"); + if (!path) path = g_cfg.path; + + return resolve_in_path_string(cmd, path); +} + +static int resolve_command(const char *cmd, char *out, int out_len) { + char *resolved = resolve_command_path(cmd, NULL); + + if (!resolved) return g_resolve_status; + + str_copy(out, resolved, out_len); + return 0; } static void build_args_string(int argc, char *argv[], int start, char *out, int out_len) { @@ -783,7 +869,7 @@ static int collect_command_matches(const char *prefix, char matches[][MAX_MATCH_ int count = 0; const char *builtins[] = { "cd", "pwd", "ls", "cat", "echo", "clear", "mkdir", "rm", - "touch", "cp", "mv", "man", "alias", "unalias", ".", "exit" + "touch", "cp", "mv", "man", "alias", "unalias", "time", ".", "exit" }; for (int i = 0; i < (int)(sizeof(builtins) / sizeof(builtins[0])); i++) { if (starts_with(builtins[i], prefix)) count = add_match_unique(matches, count, builtins[i]); @@ -897,22 +983,145 @@ static void show_matches(const char *prompt_tmpl, const char *line, int len, cha redraw_input(prompt_tmpl, line, len, len); } -static void wait_for_pid(int pid) { +static int wait_for_pid_status(int pid, int *status) { while (1) { - int rc = sys_waitpid(pid, NULL, 1); - if (rc == pid || rc < 0) break; + int child_status = 0; + int rc = sys_waitpid(pid, &child_status, 1); + if (rc == pid) { + if (status) *status = child_status; + return 0; + } + if (rc < 0) return -1; if (g_tty_id >= 0) { int fg = sys_tty_get_fg(g_tty_id); - if (fg != pid) break; + if (fg != pid) return -1; } sleep(10); } } +static void wait_for_pid(int pid) { + wait_for_pid_status(pid, NULL); +} + static void cmd_clear(void) { sys_write(1, "\x1b[2J\x1b[H", 7); } +static void print_command_resolution_error(const char *who, const char *cmd, int res) { + set_color(g_color_error); + if (res == -2) { + printf("%s: is a directory: %s\n", who, cmd); + } else if (res == -3) { + printf("%s: not executable: %s\n", who, cmd); + } else { + printf("%s: command not found: %s\n", who, cmd); + } + reset_color(); +} + +static void builtin_time_usage(void) { + printf("Usage: time [args...]\n"); + printf("\n"); + printf("Examples:\n"); + printf(" time ls\n"); + printf(" time hexdump file.txt\n"); + printf(" time /bin/hexdump.elf file.txt\n"); +} + +// Reads the system uptime in milliseconds by parsing /proc/uptime +// before and after running the command, then calculating the difference. +static unsigned long long read_uptime_ms(void) { + char buf[64]; + int fd; + int bytes; + int seconds; + + fd = sys_open("/proc/uptime", "r"); + if (fd < 0) return 0; + + bytes = sys_read(fd, buf, sizeof(buf) - 1); + sys_close(fd); + + if (bytes <= 0) return 0; + + buf[bytes] = 0; + seconds = atoi(buf); + + return (unsigned long long)seconds * 1000ULL; +} + +static int builtin_time(int argc, char *argv[]) { + char *resolved; + char full_path[256]; + char args_buf[256]; + char cmdline[MAX_LINE]; + unsigned long long start; + unsigned long long end; + unsigned long long elapsed; + int pid = -1; + int ret = -1; + + if (argc < 2) { + builtin_time_usage(); + return 1; + } + + if (str_eq(argv[1], "-h") || str_eq(argv[1], "--help")) { + builtin_time_usage(); + return 0; + } + + resolved = resolve_command_path(argv[1], NULL); + if (!resolved) { + print_command_resolution_error("time", argv[1], g_resolve_status); + return 1; + } + str_copy(full_path, resolved, sizeof(full_path)); + + build_args_string(argc, argv, 2, args_buf, sizeof(args_buf)); + + str_copy(cmdline, full_path, sizeof(cmdline)); + if (args_buf[0]) { + str_append(cmdline, " ", sizeof(cmdline)); + str_append(cmdline, args_buf, sizeof(cmdline)); + } + + start = read_uptime_ms(); + + for (int attempt = 0; attempt < 5; attempt++) { + pid = sys_spawn(full_path, args_buf[0] ? args_buf : NULL, SPAWN_FLAG_TERMINAL | SPAWN_FLAG_INHERIT_TTY, 0); + if (pid >= 0) break; + sleep(10); + } + + if (pid >= 0) { + if (g_tty_id >= 0) sys_tty_set_fg(g_tty_id, pid); + if (wait_for_pid_status(pid, &ret) != 0) ret = -1; + if (g_tty_id >= 0) sys_tty_set_fg(g_tty_id, 0); + } + + end = read_uptime_ms(); + + if (end >= start) elapsed = end - start; + else elapsed = 0; + + printf("\n"); + printf("Command: %s\n", cmdline); + printf("Exit code: %d\n", ret); + + if (ret == -1) { + printf("Command failed with non-zero exit code, not reporting time.\n"); + return ret; + } + + printf("Elapsed: %llu ms\n", elapsed); + + sys_system(SYSTEM_CMD_SLEEP, 1, 0, 0, 0); + + return ret; +} + static int builtin_cd(int argc, char *argv[]) { const char *path = (argc > 1) ? argv[1] : "/"; if (chdir(path) != 0) { @@ -1292,6 +1501,7 @@ static int execute_builtin(int argc, char *argv[]) { if (str_eq(argv[0], "man")) return builtin_man(argc, argv); if (str_eq(argv[0], "alias")) return builtin_alias(argc, argv); if (str_eq(argv[0], "unalias")) return builtin_unalias(argc, argv); + if (str_eq(argv[0], "time")) return builtin_time(argc, argv); if (str_eq(argv[0], ".")) { if (argc < 2) { set_color(g_color_error); @@ -1347,22 +1557,8 @@ static int execute_line_inner(const char *line, int depth) { char full_path[256]; int cmd_res = resolve_command(argv[0], full_path, sizeof(full_path)); - if (cmd_res == -2) { - set_color(g_color_error); - printf("bsh: is a directory: %s\n", argv[0]); - reset_color(); - return 1; - } - if (cmd_res == -3) { - set_color(g_color_error); - printf("bsh: not executable: %s\n", argv[0]); - reset_color(); - return 1; - } if (cmd_res != 0) { - set_color(g_color_error); - printf("bsh: command not found: %s\n", argv[0]); - reset_color(); + print_command_resolution_error("bsh", argv[0], cmd_res); return 1; } diff --git a/src/userland/cli/time.c b/src/userland/cli/time.c deleted file mode 100644 index cfd13db..0000000 --- a/src/userland/cli/time.c +++ /dev/null @@ -1,144 +0,0 @@ -// Copyright (c) 2026 Lluciocc (https://github.com/lluciocc) -// This software is released under the GNU General Public License v3.0. See LICENSE file for details. -// This header needs to maintain in any file it is present in, as per the GPL license terms. - -#include "stdlib.h" -#include "syscall.h" - -// CMDLINE_MAX includes the trailing NUL, so at most 511 command-line bytes can -// be reconstructed here. Unchecked concatenation can overflow CMDLINE_MAX; all -// command-line construction in this file must go through the checked append -// helpers below. -#define CMDLINE_MAX 512 - -static int has_slash(const char *s) { - while (s && *s) { - if (*s == '/') - return 1; - s++; - } - - return 0; -} - -static int ends_with_elf(const char *s) { - int len; - - if (!s) - return 0; - - len = strlen(s); - - if (len < 4) - return 0; - - return strcmp(s + len - 4, ".elf") == 0; -} - -static void print_usage(void) { - printf("Usage: time [args...]\n"); - printf("\n"); - printf("Examples:\n"); - printf(" time ls\n"); - printf(" time hexdump file.txt\n"); - printf(" time /bin/hexdump.elf file.txt\n"); -} - -// Read the system uptime in milliseconds by reading /proc/uptime and parsing the first number (seconds). -static unsigned long long read_uptime_ms(void) { - char buf[64]; - int fd; - int bytes; - int seconds; - - fd = sys_open("/proc/uptime", "r"); - - if (fd < 0) - return 0; - - bytes = sys_read(fd, buf, sizeof(buf) - 1); - - sys_close(fd); - - if (bytes <= 0) - return 0; - - buf[bytes] = 0; - - seconds = atoi(buf); - - return (unsigned long long)seconds * 1000ULL; -} - -// Build the command line for execution -// If the first argument contains a slash, use it as is. Otherwise, prepend "/bin/" and append ".elf" if it doesn't already end with ".elf". -static void build_command_line(int argc, char **argv, char *out) { - int i; - - out[0] = 0; - - if (has_slash(argv[1])) { - strcat(out, argv[1]); - } else { - strcat(out, "/bin/"); - strcat(out, argv[1]); - - if (!ends_with_elf(argv[1])) { - strcat(out, ".elf"); - } - } - - for (i = 2; i < argc; i++) { - strcat(out, " "); - strcat(out, argv[i]); - } -} - -int main(int argc, char **argv) { - char cmdline[CMDLINE_MAX]; - - unsigned long long start; - unsigned long long end; - unsigned long long elapsed; - - int ret; - - if (argc < 2) { - print_usage(); - return 1; - } - - if (strcmp(argv[1], "-h") == 0 || - strcmp(argv[1], "--help") == 0) { - print_usage(); - return 0; - } - - build_command_line(argc, argv, cmdline); - - start = read_uptime_ms(); - - ret = system(cmdline); - - end = read_uptime_ms(); - - if (end >= start) - elapsed = end - start; - else - elapsed = 0; - - printf("\n"); - printf("Command: %s\n", cmdline); - printf("Exit code: %d\n", ret); - - if (ret == -1) { - printf("Command failed with non-zero exit code, not reporting time.\n"); - return ret; - } - - printf("Elapsed: %llu ms\n", elapsed); - - sys_system(SYSTEM_CMD_SLEEP, 1, 0, 0, 0); - - return ret; -} From 7339183bf15f93a87485e71492ed7cee9f394af1 Mon Sep 17 00:00:00 2001 From: Lluciocc <114759545+Lluciocc@users.noreply.github.com> Date: Thu, 14 May 2026 01:00:15 +0200 Subject: [PATCH 13/16] Add 'time' command to terminal usage documentation --- docs/usage/terminal.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/usage/terminal.md b/docs/usage/terminal.md index 8cf7b37..5f84cf2 100644 --- a/docs/usage/terminal.md +++ b/docs/usage/terminal.md @@ -132,6 +132,7 @@ Below are some of the most used commands available in `/bin`: | `lsblk` | List block devices and partitions. | | `du` | Report disk usage for files and directories. | | `sysfetch` | Display system information and BoredOS branding. | +| `time` | Measure command execution time. | --- From 46566c766cd0dd83a95b960c6b940668dbf819d9 Mon Sep 17 00:00:00 2001 From: Lluciocc <114759545+Lluciocc@users.noreply.github.com> Date: Thu, 14 May 2026 10:20:56 +0200 Subject: [PATCH 14/16] Update bsh.c --- src/userland/cli/bsh.c | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/userland/cli/bsh.c b/src/userland/cli/bsh.c index 5e8262e..529823b 100644 --- a/src/userland/cli/bsh.c +++ b/src/userland/cli/bsh.c @@ -1033,10 +1033,6 @@ static int bsh_open_file(const char *path, const char *mode, bool *is_kernel) { return sys_open(path, mode); } -static void wait_for_pid(int pid) { - wait_for_pid_status(pid, NULL); -} - static void cmd_clear(void) { sys_write(1, "\x1b[2J\x1b[H", 7); } From 478af73f63671a869561be197205b51812067c46 Mon Sep 17 00:00:00 2001 From: Jan Date: Thu, 14 May 2026 12:57:16 +0200 Subject: [PATCH 15/16] Add GPL license header to find.c Add copyright header and license information --- src/userland/cli/find.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/userland/cli/find.c b/src/userland/cli/find.c index cae8dee..d7abec4 100644 --- a/src/userland/cli/find.c +++ b/src/userland/cli/find.c @@ -1,3 +1,6 @@ +// Copyright (c) 2026 janevers or viper0727 on discord (https://github.com/janevers-sys) +// This software is released under the GNU General Public License v3.0. See LICENSE file for details. +// This header needs to maintain in any file it is present in, as per the GPL license terms. #include #include #include "syscall.h" From faf56e56d9f17efc6e11fe45e3cc4c7b6b2203fa Mon Sep 17 00:00:00 2001 From: Jan Date: Thu, 14 May 2026 13:06:02 +0200 Subject: [PATCH 16/16] Update copyright notice in find.c --- src/userland/cli/find.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/userland/cli/find.c b/src/userland/cli/find.c index d7abec4..49b4199 100644 --- a/src/userland/cli/find.c +++ b/src/userland/cli/find.c @@ -1,4 +1,4 @@ -// Copyright (c) 2026 janevers or viper0727 on discord (https://github.com/janevers-sys) +// Copyright (c) 2026 janevers — Discord: viper0727 (https://github.com/janevers-sys) // This software is released under the GNU General Public License v3.0. See LICENSE file for details. // This header needs to maintain in any file it is present in, as per the GPL license terms. #include