From 9830b6ad9698803567489a6b41ed1b63f8ea79fe Mon Sep 17 00:00:00 2001 From: boreddevnl Date: Fri, 17 Apr 2026 11:25:45 +0200 Subject: [PATCH 1/9] VER: 26.4.2 --> 26.4.2.1 --- disk.img | Bin 67108864 -> 67108864 bytes src/core/version.c | 2 +- 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/disk.img b/disk.img index afd2c646784dd5e5a67ce886653cd5942eafc419..c560b659098cbabbb61637bd56342f94a571c18f 100644 GIT binary patch delta 4202 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 UZ}^TM_=#WmjX%zjigT{>FQ+heo&W#< diff --git a/src/core/version.c b/src/core/version.c index 6aee168..6f44835 100644 --- a/src/core/version.c +++ b/src/core/version.c @@ -13,7 +13,7 @@ void get_os_info(os_info_t *info) { for (size_t i = 0; i < sizeof(os_info_t); i++) p[i] = 0; const char *os_name = "BoredOS"; - const char *os_version = "26.4.2"; + const char *os_version = "26.4.2.1"; const char *os_codename = "Voyager"; const char *kernel_name = "Boredkernel"; const char *kernel_version = "4.1.0-stable"; From 15a7465019c563bce65947bb47a150b72a22faa3 Mon Sep 17 00:00:00 2001 From: boreddevnl Date: Fri, 17 Apr 2026 11:40:17 +0200 Subject: [PATCH 2/9] DOC: Capitalize BoredDevNL in README --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index ecd7e60..8d592ed 100644 --- a/README.md +++ b/README.md @@ -80,7 +80,7 @@ While BrewKernel served as the foundational learning ground for this OS, it has ## Contributors -- **boreddevnl** — Project creator and lead maintainer. +- **BoredDevNL** — Project creator and lead maintainer. - **Lluciocc** — Contributor. ## License From 0a8f913045f7b24391d09597acac9c144d4006de Mon Sep 17 00:00:00 2001 From: boreddevnl Date: Fri, 17 Apr 2026 11:50:42 +0200 Subject: [PATCH 3/9] DOC: Updated typo in copyright date --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 8d592ed..40cf789 100644 --- a/README.md +++ b/README.md @@ -85,7 +85,7 @@ While BrewKernel served as the foundational learning ground for this OS, it has ## License -**Copyright (C) 2024-2026 boreddevnl** +**Copyright (C) 2023-2026 boreddevnl** Distributed under the **GNU General Public License v3**. See the `LICENSE` file for details. From 9357f82d175181ecb4efe86870374e70b278be82 Mon Sep 17 00:00:00 2001 From: boreddevnl Date: Fri, 17 Apr 2026 13:09:51 +0200 Subject: [PATCH 4/9] FEAT: added colored prompts, a right aligned prompt and minimal history to Bsh --- src/library/bsh/bshrc | 16 ++- src/userland/cli/bsh.c | 267 +++++++++++++++++++++++++++++++---------- 2 files changed, 216 insertions(+), 67 deletions(-) diff --git a/src/library/bsh/bshrc b/src/library/bsh/bshrc index 225725a..a59c49e 100644 --- a/src/library/bsh/bshrc +++ b/src/library/bsh/bshrc @@ -19,9 +19,21 @@ BOOT_SCRIPT=/Library/bsh/boot.bsh # %h = hostname # %~ = cwd ("~" for /root) # %T = time (HH:MM) -PROMPT_LEFT=%n@%h:%~$ +# Colors: +# %{color} switches text color; use names or hex. +# Names: default, red, green, yellow, blue, magenta, cyan, white, gray +# Hex: #RRGGBB, 0xRRGGBB, or 0xAARRGGBB +PROMPT_LEFT=%{green}%n@%h%{default}:%~%{blue}$%{default} PROMPT_RIGHT=%T +# Minimal history mode +# When enabled, the entered line is rewritten as PROMPT_MINIMAL_PREFIX + command. +# Example: +# root@boredos:~$ echo hi +# > echo hi +PROMPT_MINIMAL_HISTORY=true +PROMPT_MINIMAL_PREFIX="> " + # History settings. HISTORY_FILE=/Library/bsh/history HISTORY_SIZE=200 @@ -31,5 +43,3 @@ GLOB=true COMPLETE=true SUGGEST=true -# Example aliases (future support). -# alias ll=ls -la diff --git a/src/userland/cli/bsh.c b/src/userland/cli/bsh.c index 2c07eb8..401ce3c 100644 --- a/src/userland/cli/bsh.c +++ b/src/userland/cli/bsh.c @@ -24,8 +24,10 @@ typedef struct { char boot_script[256]; char prompt_left[128]; char prompt_right[128]; + char prompt_minimal_prefix[64]; char history_file[128]; int history_size; + bool prompt_minimal_history; bool glob_enabled; bool complete_enabled; bool suggest_enabled; @@ -54,6 +56,69 @@ static uint32_t g_color_error = 0; static uint32_t g_color_success = 0; static uint32_t g_color_default = 0; +static bool str_eq(const char *a, const char *b); +static void get_time_string(char *out, int max_len); + +static int hex_digit(char c) { + if (c >= '0' && c <= '9') return c - '0'; + if (c >= 'a' && c <= 'f') return 10 + (c - 'a'); + if (c >= 'A' && c <= 'F') return 10 + (c - 'A'); + return -1; +} + +static bool parse_hex_color(const char *s, int len, uint32_t *out) { + if (!s || len <= 0 || !out) return false; + if (len == 7 && s[0] == '#') { + uint32_t rgb = 0; + for (int i = 1; i < 7; i++) { + int d = hex_digit(s[i]); + if (d < 0) return false; + rgb = (rgb << 4) | (uint32_t)d; + } + *out = 0xFF000000 | rgb; + return true; + } + if (len == 8 && s[0] == '0' && (s[1] == 'x' || s[1] == 'X')) { + uint32_t rgb = 0; + for (int i = 2; i < 8; i++) { + int d = hex_digit(s[i]); + if (d < 0) return false; + rgb = (rgb << 4) | (uint32_t)d; + } + *out = 0xFF000000 | rgb; + return true; + } + if (len == 10 && s[0] == '0' && (s[1] == 'x' || s[1] == 'X')) { + uint32_t argb = 0; + for (int i = 2; i < 10; i++) { + int d = hex_digit(s[i]); + if (d < 0) return false; + argb = (argb << 4) | (uint32_t)d; + } + *out = argb; + return true; + } + return false; +} + +static bool parse_named_color(const char *s, uint32_t *out) { + if (!s || !out) return false; + if (str_eq(s, "default")) { + *out = g_color_default; + return true; + } + if (str_eq(s, "black")) { *out = 0xFF000000; return true; } + if (str_eq(s, "red")) { *out = 0xFFFF4444; return true; } + if (str_eq(s, "green")) { *out = 0xFF6A9955; return true; } + if (str_eq(s, "yellow")) { *out = 0xFFFFCC00; return true; } + if (str_eq(s, "blue")) { *out = 0xFF569CD6; return true; } + if (str_eq(s, "magenta")) { *out = 0xFFC586C0; return true; } + if (str_eq(s, "cyan")) { *out = 0xFF4EC9B0; return true; } + if (str_eq(s, "white")) { *out = 0xFFFFFFFF; return true; } + if (str_eq(s, "gray")) { *out = 0xFFCCCCCC; return true; } + return false; +} + static void str_copy(char *dst, const char *src, int max_len) { int i = 0; if (max_len <= 0) return; @@ -159,8 +224,10 @@ static void config_defaults(void) { str_copy(g_cfg.boot_script, "", sizeof(g_cfg.boot_script)); str_copy(g_cfg.prompt_left, DEFAULT_PROMPT, sizeof(g_cfg.prompt_left)); str_copy(g_cfg.prompt_right, "", sizeof(g_cfg.prompt_right)); + str_copy(g_cfg.prompt_minimal_prefix, "> ", sizeof(g_cfg.prompt_minimal_prefix)); str_copy(g_cfg.history_file, "/Library/bsh/history", sizeof(g_cfg.history_file)); g_cfg.history_size = 200; + g_cfg.prompt_minimal_history = false; g_cfg.glob_enabled = true; g_cfg.complete_enabled = true; g_cfg.suggest_enabled = true; @@ -220,6 +287,88 @@ static void reset_color(void) { sys_set_text_color(g_color_default); } +static void prompt_emit(const char *text, int len, char *out, int *out_idx, int max_len, bool do_write) { + if (!text || len <= 0) return; + if (do_write) sys_write(1, text, len); + if (!out || max_len <= 0 || !out_idx) return; + for (int i = 0; i < len && *out_idx < max_len - 1; i++) { + out[(*out_idx)++] = text[i]; + } +} + +static void render_prompt(const char *tmpl, char *out, int max_len, bool do_write) { + char cwd[256]; + if (!getcwd(cwd, sizeof(cwd))) str_copy(cwd, "/", sizeof(cwd)); + + int out_idx = 0; + for (int i = 0; tmpl[i] && (!out || out_idx < max_len - 1); i++) { + if (tmpl[i] == '%' && tmpl[i + 1]) { + if (tmpl[i + 1] == '{') { + int j = i + 2; + while (tmpl[j] && tmpl[j] != '}') j++; + if (tmpl[j] == '}') { + char color_buf[32]; + int len = j - (i + 2); + if (len > (int)sizeof(color_buf) - 1) len = (int)sizeof(color_buf) - 1; + for (int k = 0; k < len; k++) color_buf[k] = tmpl[i + 2 + k]; + color_buf[len] = 0; + trim(color_buf); + + uint32_t color = 0; + if (parse_hex_color(color_buf, (int)strlen(color_buf), &color) || + parse_named_color(color_buf, &color)) { + if (do_write) set_color(color); + i = j; + continue; + } + } + } + + char token = tmpl[i + 1]; + if (token == '~') { + if (starts_with(cwd, "/root") && (cwd[5] == 0 || cwd[5] == '/')) { + prompt_emit("~", 1, out, &out_idx, max_len, do_write); + int j = 5; + while (cwd[j]) { + prompt_emit(&cwd[j], 1, out, &out_idx, max_len, do_write); + j++; + } + } else { + int j = 0; + while (cwd[j]) { + prompt_emit(&cwd[j], 1, out, &out_idx, max_len, do_write); + j++; + } + } + i++; + continue; + } + if (token == 'n') { + const char *user = "root"; + prompt_emit(user, (int)strlen(user), out, &out_idx, max_len, do_write); + i++; + continue; + } + if (token == 'h') { + const char *host = "boredos"; + prompt_emit(host, (int)strlen(host), out, &out_idx, max_len, do_write); + i++; + continue; + } + if (token == 'T') { + char time_buf[16]; + get_time_string(time_buf, sizeof(time_buf)); + prompt_emit(time_buf, (int)strlen(time_buf), out, &out_idx, max_len, do_write); + i++; + continue; + } + } + prompt_emit(&tmpl[i], 1, out, &out_idx, max_len, do_write); + } + if (out && max_len > 0) out[out_idx] = 0; + if (do_write) reset_color(); +} + static void config_load(void) { config_defaults(); @@ -284,6 +433,8 @@ static void config_load(void) { else if (str_eq(key, "BOOT_SCRIPT")) str_copy(g_cfg.boot_script, val, sizeof(g_cfg.boot_script)); else if (str_eq(key, "PROMPT_LEFT")) str_copy(g_cfg.prompt_left, val, sizeof(g_cfg.prompt_left)); else if (str_eq(key, "PROMPT_RIGHT")) str_copy(g_cfg.prompt_right, val, sizeof(g_cfg.prompt_right)); + else if (str_eq(key, "PROMPT_MINIMAL_HISTORY")) parse_bool(val, &g_cfg.prompt_minimal_history); + else if (str_eq(key, "PROMPT_MINIMAL_PREFIX")) str_copy(g_cfg.prompt_minimal_prefix, val, sizeof(g_cfg.prompt_minimal_prefix)); else if (str_eq(key, "HISTORY_FILE")) str_copy(g_cfg.history_file, val, sizeof(g_cfg.history_file)); else if (str_eq(key, "HISTORY_SIZE")) g_cfg.history_size = atoi(val); else if (str_eq(key, "GLOB")) parse_bool(val, &g_cfg.glob_enabled); @@ -379,55 +530,7 @@ static void get_time_string(char *out, int max_len) { } static void format_prompt(const char *tmpl, char *out, int max_len) { - char cwd[256]; - if (!getcwd(cwd, sizeof(cwd))) str_copy(cwd, "/", sizeof(cwd)); - - int out_idx = 0; - for (int i = 0; tmpl[i] && out_idx < max_len - 1; i++) { - if (tmpl[i] == '%' && tmpl[i + 1]) { - char token = tmpl[i + 1]; - if (token == '~') { - if (starts_with(cwd, "/root") && (cwd[5] == 0 || cwd[5] == '/')) { - out[out_idx++] = '~'; - int j = 5; - while (cwd[j] && out_idx < max_len - 1) { - out[out_idx++] = cwd[j++]; - } - } else { - int j = 0; - while (cwd[j] && out_idx < max_len - 1) { - out[out_idx++] = cwd[j++]; - } - } - i++; - continue; - } - if (token == 'n') { - const char *user = "root"; - int j = 0; - while (user[j] && out_idx < max_len - 1) out[out_idx++] = user[j++]; - i++; - continue; - } - if (token == 'h') { - const char *host = "boredos"; - int j = 0; - while (host[j] && out_idx < max_len - 1) out[out_idx++] = host[j++]; - i++; - continue; - } - if (token == 'T') { - char time_buf[16]; - get_time_string(time_buf, sizeof(time_buf)); - int j = 0; - while (time_buf[j] && out_idx < max_len - 1) out[out_idx++] = time_buf[j++]; - i++; - continue; - } - } - out[out_idx++] = tmpl[i]; - } - out[out_idx] = 0; + render_prompt(tmpl, out, max_len, false); } static int split_args(char *line, char *argv[], int max_args) { @@ -677,21 +780,49 @@ static int collect_path_matches(const char *dir_part, const char *prefix, char m return count; } -static void redraw_input(const char *prompt, const char *line, int len) { +static void prompt_write(const char *tmpl) { + render_prompt(tmpl, NULL, 0, true); +} + +static void prompt_write_with_right(const char *left_tmpl, const char *right_tmpl) { + prompt_write(left_tmpl); + if (!right_tmpl || !right_tmpl[0]) return; + + char right_buf[128]; + render_prompt(right_tmpl, right_buf, sizeof(right_buf), false); + int right_len = (int)strlen(right_buf); + if (right_len <= 0) return; + + sys_write(1, "\x1b[s", 3); + sys_write(1, "\x1b[999C", 6); + if (right_len > 1) { + char num[8]; + char seq[16]; + itoa(right_len - 1, num); + str_copy(seq, "\x1b[", sizeof(seq)); + str_append(seq, num, sizeof(seq)); + str_append(seq, "D", sizeof(seq)); + sys_write(1, seq, (int)strlen(seq)); + } + prompt_write(right_tmpl); + sys_write(1, "\x1b[u", 3); +} + +static void redraw_input(const char *prompt_tmpl, const char *line, int len) { sys_write(1, "\r", 1); - sys_write(1, prompt, (int)strlen(prompt)); + prompt_write_with_right(prompt_tmpl, g_cfg.prompt_right); sys_write(1, line, len); sys_write(1, "\x1b[K", 3); } -static void show_matches(const char *prompt, const char *line, int len, char matches[][MAX_MATCH_LEN], int count) { +static void show_matches(const char *prompt_tmpl, const char *line, int len, char matches[][MAX_MATCH_LEN], int count) { sys_write(1, "\n", 1); for (int i = 0; i < count; i++) { sys_write(1, matches[i], (int)strlen(matches[i])); sys_write(1, " ", 2); } sys_write(1, "\n", 1); - redraw_input(prompt, line, len); + redraw_input(prompt_tmpl, line, len); } static int pid_exists(int pid) { @@ -1234,7 +1365,7 @@ static bool run_script(const char *path) { return true; } -static int read_line(char *out, int max_len, const char *prompt) { +static int read_line(char *out, int max_len, const char *prompt_tmpl) { int len = 0; int hist_index = g_history_count; bool search_mode = false; @@ -1260,7 +1391,16 @@ static int read_line(char *out, int max_len, const char *prompt) { } if (ch == '\r' || ch == '\n') { - sys_write(1, "\n", 1); + if (g_cfg.prompt_minimal_history) { + const char *minimal_tmpl = g_cfg.prompt_minimal_prefix[0] ? g_cfg.prompt_minimal_prefix : "> "; + sys_write(1, "\r", 1); + sys_write(1, "\x1b[K", 3); + prompt_write(minimal_tmpl); + sys_write(1, out, len); + sys_write(1, "\n", 1); + } else { + sys_write(1, "\n", 1); + } out[len] = 0; return len; } @@ -1322,7 +1462,7 @@ static int read_line(char *out, int max_len, const char *prompt) { out[token_start] = 0; str_append(out, matches[0], max_len); len = (int)strlen(out); - redraw_input(prompt, out, len); + redraw_input(prompt_tmpl, out, len); } else { int common_len = common_prefix_len(matches, match_count); if (common_len > token_len) { @@ -1333,9 +1473,9 @@ static int read_line(char *out, int max_len, const char *prompt) { out[token_start] = 0; str_append(out, prefix_buf, max_len); len = (int)strlen(out); - redraw_input(prompt, out, len); + redraw_input(prompt_tmpl, out, len); } else { - show_matches(prompt, out, len, matches, match_count); + show_matches(prompt_tmpl, out, len, matches, match_count); } } search_mode = false; @@ -1363,7 +1503,7 @@ static int read_line(char *out, int max_len, const char *prompt) { hist_index = found; str_copy(out, g_history[hist_index], max_len); len = (int)strlen(out); - redraw_input(prompt, out, len); + redraw_input(prompt_tmpl, out, len); } continue; } @@ -1387,7 +1527,7 @@ static int read_line(char *out, int max_len, const char *prompt) { str_copy(out, saved_line, max_len); len = (int)strlen(out); } - redraw_input(prompt, out, len); + redraw_input(prompt_tmpl, out, len); continue; } @@ -1438,12 +1578,11 @@ int main(int argc, char **argv) { while (1) { if (g_tty_id >= 0) sys_tty_set_fg(g_tty_id, 0); - char prompt[128]; - format_prompt(g_cfg.prompt_left[0] ? g_cfg.prompt_left : DEFAULT_PROMPT, prompt, sizeof(prompt)); - sys_write(1, prompt, (int)strlen(prompt)); + const char *prompt_tmpl = g_cfg.prompt_left[0] ? g_cfg.prompt_left : DEFAULT_PROMPT; + prompt_write_with_right(prompt_tmpl, g_cfg.prompt_right); char line[MAX_LINE]; - int len = read_line(line, sizeof(line), prompt); + int len = read_line(line, sizeof(line), prompt_tmpl); if (len <= 0) continue; history_add(line); From 89140d754625cf89700a2a8e3ab0a901e0d6a5dd Mon Sep 17 00:00:00 2001 From: boreddevnl Date: Fri, 17 Apr 2026 13:35:40 +0200 Subject: [PATCH 5/9] FIX: Cursor misalignment when PROMPT_RIGHT was enabled --- src/library/bsh/bshrc | 2 +- src/userland/cli/bsh.c | 83 ++++++++++++++++++++++++++++++++++++++---- 2 files changed, 77 insertions(+), 8 deletions(-) diff --git a/src/library/bsh/bshrc b/src/library/bsh/bshrc index a59c49e..3e988bc 100644 --- a/src/library/bsh/bshrc +++ b/src/library/bsh/bshrc @@ -5,7 +5,7 @@ # Lines starting with # are comments. # # PATH controls command lookup order (colon-separated). -PATH=/bin:/root/Apps +export PATH="/bin" # Scripts to run automatically. # STARTUP runs for interactive shells. diff --git a/src/userland/cli/bsh.c b/src/userland/cli/bsh.c index 401ce3c..b7bf043 100644 --- a/src/userland/cli/bsh.c +++ b/src/userland/cli/bsh.c @@ -49,6 +49,7 @@ static alias_t g_aliases[MAX_ALIASES]; static int g_alias_count = 0; static int g_tty_id = -1; +static bool g_need_prompt_newline = false; static uint32_t g_color_dir = 0; static uint32_t g_color_file = 0; static uint32_t g_color_size = 0; @@ -183,6 +184,42 @@ static void trim(char *s) { s[out] = 0; } +static void strip_quotes(char *s) { + if (!s) return; + int len = (int)strlen(s); + if (len >= 2 && ((s[0] == '"' && s[len - 1] == '"') || (s[0] == '\'' && s[len - 1] == '\''))) { + for (int i = 1; i < len - 1; i++) { + s[i - 1] = s[i]; + } + s[len - 2] = 0; + } +} + +static void expand_path_value(const char *val, char *out, int max_len) { + if (!out || max_len <= 0) return; + out[0] = 0; + if (!val) return; + + const char *needle1 = "$PATH"; + const char *needle2 = "${PATH}"; + int i = 0; + while (val[i] && (int)strlen(out) < max_len - 1) { + if (starts_with(&val[i], needle1)) { + str_append(out, g_cfg.path, max_len); + i += (int)strlen(needle1); + continue; + } + if (starts_with(&val[i], needle2)) { + str_append(out, g_cfg.path, max_len); + i += (int)strlen(needle2); + continue; + } + char ch[2] = { val[i], 0 }; + str_append(out, ch, max_len); + i++; + } +} + static void alias_add(const char *name, const char *value) { if (!name || !name[0] || !value) return; for (int i = 0; i < g_alias_count; i++) { @@ -419,16 +456,27 @@ static void config_load(void) { continue; } - char *sep = line; + char *assign = line; + if (starts_with(assign, "export ") || starts_with(assign, "export\t")) { + assign += 6; + while (*assign == ' ' || *assign == '\t') assign++; + } + + char *sep = assign; while (*sep && *sep != '=') sep++; if (*sep == '=') { *sep = 0; - char *key = line; + char *key = assign; char *val = sep + 1; trim(key); trim(val); + strip_quotes(val); - if (str_eq(key, "PATH")) str_copy(g_cfg.path, val, sizeof(g_cfg.path)); + if (str_eq(key, "PATH")) { + char expanded[256]; + expand_path_value(val, expanded, sizeof(expanded)); + str_copy(g_cfg.path, expanded[0] ? expanded : val, sizeof(g_cfg.path)); + } else if (str_eq(key, "STARTUP")) str_copy(g_cfg.startup, val, sizeof(g_cfg.startup)); else if (str_eq(key, "BOOT_SCRIPT")) str_copy(g_cfg.boot_script, val, sizeof(g_cfg.boot_script)); else if (str_eq(key, "PROMPT_LEFT")) str_copy(g_cfg.prompt_left, val, sizeof(g_cfg.prompt_left)); @@ -785,6 +833,10 @@ static void prompt_write(const char *tmpl) { } static void prompt_write_with_right(const char *left_tmpl, const char *right_tmpl) { + char left_buf[128]; + render_prompt(left_tmpl, left_buf, sizeof(left_buf), false); + int left_len = (int)strlen(left_buf); + prompt_write(left_tmpl); if (!right_tmpl || !right_tmpl[0]) return; @@ -793,19 +845,28 @@ static void prompt_write_with_right(const char *left_tmpl, const char *right_tmp int right_len = (int)strlen(right_buf); if (right_len <= 0) return; - sys_write(1, "\x1b[s", 3); + sys_write(1, "\r", 1); sys_write(1, "\x1b[999C", 6); - if (right_len > 1) { + if (right_len > 0) { char num[8]; char seq[16]; - itoa(right_len - 1, num); + itoa(right_len, num); str_copy(seq, "\x1b[", sizeof(seq)); str_append(seq, num, sizeof(seq)); str_append(seq, "D", sizeof(seq)); sys_write(1, seq, (int)strlen(seq)); } prompt_write(right_tmpl); - sys_write(1, "\x1b[u", 3); + sys_write(1, "\r", 1); + if (left_len > 0) { + char num[8]; + char seq[16]; + itoa(left_len, num); + str_copy(seq, "\x1b[", sizeof(seq)); + str_append(seq, num, sizeof(seq)); + str_append(seq, "C", sizeof(seq)); + sys_write(1, seq, (int)strlen(seq)); + } } static void redraw_input(const char *prompt_tmpl, const char *line, int len) { @@ -1327,6 +1388,7 @@ static int execute_line_inner(const char *line, int depth) { if (g_tty_id >= 0) sys_tty_set_fg(g_tty_id, pid); wait_for_pid(pid); if (g_tty_id >= 0) sys_tty_set_fg(g_tty_id, 0); + g_need_prompt_newline = true; return 0; } @@ -1579,6 +1641,12 @@ int main(int argc, char **argv) { if (g_tty_id >= 0) sys_tty_set_fg(g_tty_id, 0); const char *prompt_tmpl = g_cfg.prompt_left[0] ? g_cfg.prompt_left : DEFAULT_PROMPT; + if (g_need_prompt_newline) { + sys_write(1, "\n", 1); + g_need_prompt_newline = false; + } + sys_write(1, "\r", 1); + sys_write(1, "\x1b[K", 3); prompt_write_with_right(prompt_tmpl, g_cfg.prompt_right); char line[MAX_LINE]; @@ -1587,6 +1655,7 @@ int main(int argc, char **argv) { history_add(line); int res = execute_line(line); + if (g_cfg.prompt_minimal_history) g_need_prompt_newline = true; if (res == 2) break; } From fb00bbac2bd0fab9b8fa026e4f4f9a12e13e10d4 Mon Sep 17 00:00:00 2001 From: boreddevnl Date: Fri, 17 Apr 2026 14:13:44 +0200 Subject: [PATCH 6/9] RM: Old userspace/syscall test assembly files --- src/arch/test_syscall.asm | 19 ------------------- src/arch/user_test.asm | 25 ------------------------- 2 files changed, 44 deletions(-) delete mode 100644 src/arch/test_syscall.asm delete mode 100644 src/arch/user_test.asm diff --git a/src/arch/test_syscall.asm b/src/arch/test_syscall.asm deleted file mode 100644 index dde4d86..0000000 --- a/src/arch/test_syscall.asm +++ /dev/null @@ -1,19 +0,0 @@ -; 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. -global test_syscall -section .text - -test_syscall: - ; syscall number in RDI - mov rdi, 1 - ; string pointer in RSI - lea rsi, [rel test_msg] - - ; The SYSCALL instruction - syscall - - ret - -section .rodata -test_msg: db "Hello from Syscall!", 10, 0 diff --git a/src/arch/user_test.asm b/src/arch/user_test.asm deleted file mode 100644 index 1c51f02..0000000 --- a/src/arch/user_test.asm +++ /dev/null @@ -1,25 +0,0 @@ -; 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. -global user_test_function - -section .text -user_test_function: - ; Syscall convention -.loop: - ; Invoke SYS_WRITE (Syscall #1) - mov rdi, 1 ; arg1: fd = 1 (stdout) - lea rsi, [rel msg] ; arg2: buffer (RIP-relative) - mov rdx, 15 ; arg3: length - mov eax, 1 ; syscall_num = 1 (SYS_WRITE) - syscall - - ; Some delay loop - mov rcx, 100000000 -.delay: - dec rcx - jnz .delay - - jmp .loop - -msg: db "Hello syscall!", 10 From 8ea457694da4943b065150fef5275d62837f1819 Mon Sep 17 00:00:00 2001 From: boreddevnl Date: Fri, 17 Apr 2026 14:16:10 +0200 Subject: [PATCH 7/9] RM: Old test userspace application --- src/userland/cli/hello.c | 17 ----------------- 1 file changed, 17 deletions(-) delete mode 100644 src/userland/cli/hello.c diff --git a/src/userland/cli/hello.c b/src/userland/cli/hello.c deleted file mode 100644 index c57e67b..0000000 --- a/src/userland/cli/hello.c +++ /dev/null @@ -1,17 +0,0 @@ -// 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 -#include - -int main(int argc, char** argv) { - printf("Hello from Userland ELF!\n"); - - printf("argc: %d\n", argc); - - for (int i = 0; i < argc; i++) { - printf("argv[%d]: %s\n", i, argv[i]); - } - - return 0; -} From 3eafa5b3603fe073ed03128f26e10aac09a44dcf Mon Sep 17 00:00:00 2001 From: Chris Date: Fri, 17 Apr 2026 16:00:57 +0200 Subject: [PATCH 8/9] DOC: Update project disclaimer with BrewKernel hyperlink --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 40cf789..7f33baf 100644 --- a/README.md +++ b/README.md @@ -67,9 +67,9 @@ If you find this project interesting or helpful, consider fueling the developmen --- -## ⚠️ Project Disclaimer & Heritage +## Project Disclaimer & Heritage -**BoredOS** is the successor to **BrewKernel**, a project initiated in 2023. +**BoredOS** is the successor to **[BrewKernel](https://github.com/boreddevnl/brewkernel)**, a project initiated in 2023. While BrewKernel served as the foundational learning ground for this OS, it has been officially **deprecated and archived**. It no longer receives updates, bug fixes, or pull request reviews. BoredOS represents a complete architectural reboot, applying years of lessons learned to create a cleaner, more modular, and more capable system. From d4b066c29f05f496a64892b4c1ef8eef672e5b73 Mon Sep 17 00:00:00 2001 From: boreddevnl Date: Fri, 17 Apr 2026 23:11:06 +0200 Subject: [PATCH 9/9] FEAT: add donut.c demo by Andy Sloane --- src/userland/Makefile | 6 ++--- src/userland/cli/third_party/donut.c | 40 ++++++++++++++++++++++++++++ 2 files changed, 43 insertions(+), 3 deletions(-) create mode 100644 src/userland/cli/third_party/donut.c diff --git a/src/userland/Makefile b/src/userland/Makefile index 90b586b..4c369fa 100644 --- a/src/userland/Makefile +++ b/src/userland/Makefile @@ -13,10 +13,10 @@ BIN_DIR = bin LIBC_SOURCES = $(wildcard libc/*.c) LIBC_OBJS = $(patsubst libc/%.c, $(BIN_DIR)/%.o, $(LIBC_SOURCES)) $(BIN_DIR)/crt0.o $(BIN_DIR)/libwidget.o -VPATH = cli gui sys games libc net -vpath %.c cli gui sys games libc net +VPATH = cli gui sys games libc net cli/third_party +vpath %.c cli gui sys games libc net cli/third_party -APP_SOURCES_FULL = $(wildcard cli/*.c gui/*.c sys/*.c games/*.c *.c net/*.c) +APP_SOURCES_FULL = $(wildcard cli/*.c gui/*.c sys/*.c games/*.c *.c net/*.c cli/third_party/*.c) APP_SOURCES = $(filter-out stb_image.c, $(APP_SOURCES_FULL)) APP_ELFS = $(patsubst %.c, $(BIN_DIR)/%.elf, $(notdir $(APP_SOURCES))) diff --git a/src/userland/cli/third_party/donut.c b/src/userland/cli/third_party/donut.c new file mode 100644 index 0000000..72938d5 --- /dev/null +++ b/src/userland/cli/third_party/donut.c @@ -0,0 +1,40 @@ +/* + * donut.c - A rotating ASCII donut in C, but the code is a donut aswell. + * Created by Andy Sloane (@a1k0n) in 2006. + * * Source: https://www.a1k0n.net/2006/09/15/obfuscated-c-donut.html + * + * LICENSE NOTE: + * This file is included in BoredOS as a demonstration. + * This specific file is NOT covered by the project's GPLv3 license + * and remains the intellectual property of Andy Sloane. + * Use of this file is subject to the author's original terms. + */ +#include +#include + +int putchar(int c) { + char ch = (char)c; + return sys_write(1, &ch, 1); +} + +int k;double sin() + ,cos();int main(){float A= + 0,B=0,i,j,z[1760];char b[ + 1760];printf("\x1b[2J");for(;; + ){memset(b,32,1760);memset(z,0,7040) + ;for(j=0;6.28>j;j+=0.07)for(i=0;6.28 + >i;i+=0.02){float c=sin(i),d=cos(j),e= + sin(A),f=sin(j),g=cos(A),h=d+2,D=1/(c* + h*e+f*g+5),l=cos (i),m=cos(B),n=s\ +in(B),t=c*h*g-f* e;int x=40+30*D* +(l*h*m-t*n),y= 12+15*D*(l*h*n ++t*m),o=x+80*y, N=8*((f*e-c*d*g + )*m-c*d*e-f*g-l *d*n);if(22>y&& + y>0&&x>0&&80>x&&D>z[o]){z[o]=D;;;b[o]= + ".,-~:;=!*#$@"[N>0?N:0];}}/*#****!!-*/ + printf("\x1b[H");for(k=0;1761>k;k++) + putchar(k%80?b[k]:10);A+=0.04;B+= + 0.02;}}/*****####*******!!=;:~ + ~::==!!!**********!!!==::- + .,~~;;;========;;;:~-. + ..,--------,*/ \ No newline at end of file