diff options
| author | hachem <im@hachem.wtf> | 2026-10-08 11:50:02 +0200 |
|---|---|---|
| committer | hachem <im@hachem.wtf> | 2026-10-08 11:50:02 +0200 |
| commit | ef104148057ab98f80b1560405b5c02768457096 (patch) | |
| tree | 47e75bc81aacb2e035e7cd43be9616b677b01d04 | |
| parent | 5f6deca073728d0dafa2cd47d841b28d54af3aaf (diff) | |
feat: colors + better ps1
| -rw-r--r-- | gils/cat.rv | 2 | ||||
| -rw-r--r-- | gils/cp.rv | 4 | ||||
| -rw-r--r-- | gils/gils.rv | 2 | ||||
| -rw-r--r-- | gils/head.rv | 4 | ||||
| -rw-r--r-- | gils/mkdir.rv | 2 | ||||
| -rw-r--r-- | gils/mv.rv | 4 | ||||
| -rw-r--r-- | gils/rm.rv | 2 | ||||
| -rw-r--r-- | gils/tail.rv | 4 | ||||
| -rw-r--r-- | gils/touch.rv | 4 | ||||
| -rw-r--r-- | gils/tree.rv | 4 | ||||
| -rw-r--r-- | gils/wc.rv | 4 | ||||
| -rw-r--r-- | kernel/console.rv | 94 | ||||
| -rw-r--r-- | kernel/fs.rv | 22 | ||||
| -rw-r--r-- | kernel/shell.rv | 8 |
14 files changed, 122 insertions, 38 deletions
diff --git a/gils/cat.rv b/gils/cat.rv index 220ce93..0666304 100644 --- a/gils/cat.rv +++ b/gils/cat.rv @@ -2,7 +2,7 @@ const context = env.ctx const parsed = arg_name(context.rest, 0) -if parsed.name:len() == 0 do puts("usage: cat <path>\n") end +if parsed.name:len() == 0 do fail("usage: cat <path>\n") end else do const path = expand_path(parsed.name) if vfs_owns(path) do vfs_cat(path) end @@ -2,12 +2,12 @@ # HAAAAAANNNNKKKK const context = env.ctx -if context.argv:len() < 3 do puts("usage: cp <source> <destination>\n") end +if context.argv:len() < 3 do fail("usage: cp <source> <destination>\n") end else do const source = expand_path(context.argv[1]) const destination = expand_path(context.argv[2]) const size = fs:size(source) - if size < 0 do puts("cp: #{source}: no such file\n") end + if size < 0 do fail("cp: #{source}: no such file\n") end else do const contents = if size > 0 fs:read_bytes(source, 0, size) else "" if fs:store(destination, contents) == 1 do puts("#{source} -> #{destination}\n") end diff --git a/gils/gils.rv b/gils/gils.rv index 08c97cd..a500322 100644 --- a/gils/gils.rv +++ b/gils/gils.rv @@ -39,7 +39,7 @@ fn run_script_path(path, label, ctx) do if size < 0 return :false env.ctx = ctx const error_msg = eval_script(fs:read_bytes(path, 0, size)) - if error_msg != :nil do puts("#{label}: #{error_msg}\n") end + if error_msg != :nil do fail("#{label}: #{error_msg}\n") end return :true end diff --git a/gils/head.rv b/gils/head.rv index addd11a..2c2a82f 100644 --- a/gils/head.rv +++ b/gils/head.rv @@ -13,13 +13,13 @@ fn file_lines(path) do end const context = env.ctx -if context.argv:len() < 2 do puts("usage: head [n] <path>\n") end +if context.argv:len() < 2 do fail("usage: head [n] <path>\n") end else do let limit = 10 let path_arg = context.argv[1] if (context.argv:len() >= 3 and parse_pid(context.argv[1]) >= 0) do limit = parse_pid(context.argv[1]) path_arg = context.argv[2] end const lines = file_lines(expand_path(path_arg)) - if lines == :nil do puts("head: no such file\n") end + if lines == :nil do fail("head: no such file\n") end else do let index = 0 while (index < lines:len() and index < limit) do puts("#{lines[index]}\n") index += 1 end diff --git a/gils/mkdir.rv b/gils/mkdir.rv index 759544e..c7d12f7 100644 --- a/gils/mkdir.rv +++ b/gils/mkdir.rv @@ -1,5 +1,5 @@ # make a directory const context = env.ctx -if context.argv:len() < 2 do puts("usage: mkdir <path>\n") end +if context.argv:len() < 2 do fail("usage: mkdir <path>\n") end else do fs:mkdir(expand_path(context.argv[1])) end @@ -1,12 +1,12 @@ # move or rename a file const context = env.ctx -if context.argv:len() < 3 do puts("usage: mv <source> <destination>\n") end +if context.argv:len() < 3 do fail("usage: mv <source> <destination>\n") end else do const source = expand_path(context.argv[1]) const destination = expand_path(context.argv[2]) const size = fs:size(source) - if size < 0 do puts("mv: #{source}: no such file\n") end + if size < 0 do fail("mv: #{source}: no such file\n") end else do const contents = if size > 0 fs:read_bytes(source, 0, size) else "" if fs:store(destination, contents) == 1 do fs:unlink(source) puts("#{source} -> #{destination}\n") end @@ -2,5 +2,5 @@ const context = env.ctx const parsed = arg_name(context.rest, 0) -if parsed.name:len() == 0 do puts("usage: rm <path>\n") end +if parsed.name:len() == 0 do fail("usage: rm <path>\n") end else do fs:rm(expand_path(parsed.name)) end diff --git a/gils/tail.rv b/gils/tail.rv index 4a661b4..c1ca124 100644 --- a/gils/tail.rv +++ b/gils/tail.rv @@ -14,13 +14,13 @@ fn file_lines(path) do end const context = env.ctx -if context.argv:len() < 2 do puts("usage: tail [n] <path>\n") end +if context.argv:len() < 2 do fail("usage: tail [n] <path>\n") end else do let limit = 10 let path_arg = context.argv[1] if (context.argv:len() >= 3 and parse_pid(context.argv[1]) >= 0) do limit = parse_pid(context.argv[1]) path_arg = context.argv[2] end const lines = file_lines(expand_path(path_arg)) - if lines == :nil do puts("tail: no such file\n") end + if lines == :nil do fail("tail: no such file\n") end else do let start = lines:len() - limit if start < 0 do start = 0 end diff --git a/gils/touch.rv b/gils/touch.rv index 84ecca8..f8e3e18 100644 --- a/gils/touch.rv +++ b/gils/touch.rv @@ -1,9 +1,9 @@ # dont touch my nono square const context = env.ctx -if context.argv:len() < 2 do puts("usage: touch <path>\n") end +if context.argv:len() < 2 do fail("usage: touch <path>\n") end else do const path = expand_path(context.argv[1]) - if fs:is_dir(path) do puts("touch: #{path}: is a directory\n") end + if fs:is_dir(path) do fail("touch: #{path}: is a directory\n") end else if fs:size(path) < 0 do fs:store(path, "") end end diff --git a/gils/tree.rv b/gils/tree.rv index cc41601..d1a6af6 100644 --- a/gils/tree.rv +++ b/gils/tree.rv @@ -21,8 +21,8 @@ end const context = env.ctx const target = if context.argv:len() > 1 expand_path(context.argv[1]) else wd.path -if (vfs_owns(target) and target != "/") do puts("tree: #{target}: virtual dir\n") end -else if (target != "/" and fs:is_dir(target) == :false) do puts("tree: #{target}: not a directory\n") end +if (vfs_owns(target) and target != "/") do fail("tree: #{target}: virtual dir\n") end +else if (target != "/" and fs:is_dir(target) == :false) do fail("tree: #{target}: not a directory\n") end else do puts("#{target}\n") tree_walk(target, "") @@ -1,11 +1,11 @@ # word count const context = env.ctx -if context.argv:len() < 2 do puts("usage: wc <path>\n") end +if context.argv:len() < 2 do fail("usage: wc <path>\n") end else do const path = expand_path(context.argv[1]) const size = fs:size(path) - if size < 0 do puts("wc: #{path}: no such file\n") end + if size < 0 do fail("wc: #{path}: no such file\n") end else do const contents = if size > 0 fs:read_bytes(path, 0, size) else "" let line_count = 0 diff --git a/kernel/console.rv b/kernel/console.rv index 3bff6de..9447860 100644 --- a/kernel/console.rv +++ b/kernel/console.rv @@ -1,11 +1,54 @@ const FONT_WIDTH = 8 # glyph width in pixels const FONT_HEIGHT = 16 # glyph height in pixels (8x16 vga bitmap font) -const FG_COLOR = 0xd0d0d0 -const BG_COLOR = 0x101018 -const CURSOR_COLOR = 0x50fa7b + +# base16 from lung +const BASE00 = 0x101010 # background +const BASE03 = 0x505050 # bright black +const BASE05 = 0xd0d0d0 # default foreground +const BASE07 = 0xfafafa # bright white +const BASE08 = 0xab4642 # red +const BASE09 = 0xdc9656 # orange +const BASE0A = 0xf7ca88 # yellow +const BASE0B = 0xa1b56c # green +const BASE0C = 0x86c1b9 # cyan +const BASE0D = 0x7cafc2 # blue +const BASE0E = 0xba8baf # magenta +const FG_COLOR = BASE05 +const BG_COLOR = BASE00 +const CURSOR_COLOR = BASE0B global wd = { path = "/" } -global prompt = fn() "revel:#{wd.path}$ " +global shell_status = { code = 0 } + +global prompt = fn() do + let mid = "" + unless (shell_status.code == 0) do mid = "\x1b[31m#{shell_status.code} " end + return "\x1b[33m#{wd.path} #{mid}\x1b[97m$\x1b[0m " +end + +fn parse_int(text) do + let value = 0 + for index in 0..text:len() do + const digit = text[index]:ascii() + if (digit >= 48 and digit <= 57) do value = value * 10 + (digit - 48) end + end + return value +end + +fn sgr_color(code, current) do + if (code == 0 or code == 39) return BASE05 + if code == 30 return BASE00 + if code == 90 return BASE03 + if (code == 31 or code == 91) return BASE08 + if (code == 32 or code == 92) return BASE0B + if (code == 33 or code == 93) return BASE0A + if (code == 34 or code == 94) return BASE0D + if (code == 35 or code == 95) return BASE0E + if (code == 36 or code == 96) return BASE0C + if code == 37 return BASE05 + if code == 97 return BASE07 + return current +end # runtime font bs fn embolden(byte, row) do return (byte bor (byte shl 1)) band 0xFF end @@ -58,7 +101,23 @@ add_font("italic", remap_glyphs(slant), FONT_WIDTH, FONT_HEIGHT, :bitmap, :nil, add_font("bolditalic", remap_glyphs(fn(byte, row) do return slant(embolden(byte, row), row) end), FONT_WIDTH, FONT_HEIGHT, :bitmap, :nil, 0) global console = { - column = 0, row = 0, scale = 1, shown = :false, cursor_x = 0, cursor_y = 0, blink = 0, font = FONT, font_width = FONT_WIDTH, font_height = FONT_HEIGHT, font_entry = find_font("vga"), + column = 0, + row = 0, + scale = 1, + + shown = :false, + cursor_x = 0, + cursor_y = 0, + blink = 0, + + font = FONT, + font_width = FONT_WIDTH, + font_height = FONT_HEIGHT, + font_entry = find_font("vga"), + + fg = FG_COLOR, + ansi = 0, + ansi_param = "", # this is now font dependent cell_width = fn(self) self.font_width * self.scale, @@ -87,7 +146,7 @@ global console = { for glyph_row in 0..self.font_height do const bits = glyph[glyph_row] for glyph_col in 0..self.font_width do - if (bits shr glyph_col) band 1 == 1 fill_rect(origin_x + glyph_col * self.scale, origin_y + glyph_row * self.scale, self.scale, self.scale, FG_COLOR) + if (bits shr glyph_col) band 1 == 1 fill_rect(origin_x + glyph_col * self.scale, origin_y + glyph_row * self.scale, self.scale, self.scale, self.fg) end end end, @@ -103,8 +162,26 @@ global console = { # we dont do shift/cntrl, thats a job for future me lmao # NOTE: future me kinda delt with it, i was mad drunk + apply_sgr = fn(self, param) do + if param:len() == 0 do self.fg = BASE05 return :nil end + const parts = param:split(";") + for index in 0..parts:len() do self.fg = sgr_color(parse_int(parts[index]), self.fg) end + end, + putc = fn(self, character) do const code = character:ascii() + if self.ansi == 1 do + if code == 91 do self.ansi = 2 self.ansi_param = "" end + else do self.ansi = 0 end + return :nil + end + if self.ansi == 2 do + if (code >= 48 and code <= 57) or code == 59 do self.ansi_param = self.ansi_param ~ character return :nil end + if code == 109 do self:apply_sgr(self.ansi_param) end + self.ansi = 0 + return :nil + end + if code == 27 do self.ansi = 1 return :nil end # ESC, start a sequence if code == 10 do self:newline() return :nil end # \n if code == 13 do self.column = 0 return :nil end # \r if code == 8 do # backspace: move left @@ -146,6 +223,11 @@ global console = { global puts = fn(text) serial_puts(text) +global fail = fn(message) do + puts(message) + shell_status.code = 1 +end + global flush_console = fn() do console:cursor_clear() console:drain() diff --git a/kernel/fs.rv b/kernel/fs.rv index 255797f..23ccd78 100644 --- a/kernel/fs.rv +++ b/kernel/fs.rv @@ -249,11 +249,11 @@ global fs = { mkdir = fn(self, path) do if self:ensure() == :false return :nil const parent = self:resolve_parent(path) - if parent == :nil do puts("mkdir: #{path}: bad path\n") return :nil end + if parent == :nil do fail("mkdir: #{path}: bad path\n") return :nil end if parent.name:len() > NAME_MAX do puts("name too long (max #{NAME_MAX})\n") return :nil end - if self:dir_lookup(parent.dir, parent.name) != :nil do puts("mkdir: #{parent.name}: already exists\n") return :nil end + if self:dir_lookup(parent.dir, parent.name) != :nil do fail("mkdir: #{parent.name}: already exists\n") return :nil end const new_block = self:fat_alloc() - if new_block == 0 do puts("disk full\n") return :nil end + if new_block == 0 do fail("disk full\n") return :nil end self:fat_set(new_block, END) mem_zero(self.scratch, BLOCK) disk_write(new_block, self.scratch) @@ -267,7 +267,7 @@ global fs = { if parent.name:len() > NAME_MAX do puts("name too long (max #{NAME_MAX})\n") return 0 end const existing = self:dir_lookup(parent.dir, parent.name) if existing != :nil do - if existing.type == T_DIR do puts("#{parent.name}: is a directory\n") return 0 end + if existing.type == T_DIR do fail("#{parent.name}: is a directory\n") return 0 end self:free_chain(existing.first) end @@ -277,7 +277,7 @@ global fs = { let written = 0 while written < size do const block = self:fat_alloc() - if block == 0 do puts("disk full\n") return 0 end + if block == 0 do fail("disk full\n") return 0 end self:fat_set(block, END) if first == 0 do first = block end else do self:fat_set(previous, block) end @@ -349,8 +349,8 @@ global fs = { cat = fn(self, path) do if self:ensure() == :false return :nil const entry = self:resolve(path) - if entry == :nil do puts("cat: #{path}: no such file\n") return :nil end - if entry.type == T_DIR do puts("cat: #{path}: is a directory\n") return :nil end + if entry == :nil do fail("cat: #{path}: no such file\n") return :nil end + if entry.type == T_DIR do fail("cat: #{path}: is a directory\n") return :nil end let block = entry.first let remaining = entry.size let result = "" @@ -369,7 +369,7 @@ global fs = { ls = fn(self, path) do if self:ensure() == :false return -1 const entry = self:resolve(path) - if entry == :nil do puts("ls: #{path}: no such file\n") return -1 end + if entry == :nil do fail("ls: #{path}: no such file\n") return -1 end if entry.type != T_DIR do puts(" #{path} #{entry.size} bytes\n") return -1 end let count = 0 let block = entry.first @@ -394,11 +394,11 @@ global fs = { rm = fn(self, path) do if self:ensure() == :false return :nil const parent = self:resolve_parent(path) - if parent == :nil do puts("rm: #{path}: no such file\n") return :nil end + if parent == :nil do fail("rm: #{path}: no such file\n") return :nil end const entry = self:dir_lookup(parent.dir, parent.name) - if entry == :nil do puts("rm: #{path}: no such file\n") return :nil end + if entry == :nil do fail("rm: #{path}: no such file\n") return :nil end if entry.type == T_DIR and self:dir_empty(entry.first) == :false do - puts("rm: #{parent.name}: directory not empty\n") + fail("rm: #{parent.name}: directory not empty\n") return :nil end self:free_chain(entry.first) diff --git a/kernel/shell.rv b/kernel/shell.rv index f112889..31cc70e 100644 --- a/kernel/shell.rv +++ b/kernel/shell.rv @@ -88,7 +88,7 @@ fn cmd_cd(context) do const arg = if context.argv:len() > 1 context.argv[1] else "/" const target = if arg == ".." parent_of(wd.path) else expand_path(arg) if target == "/proc" or target == "/bin" or fs:is_dir(target) do wd.path = target end - else do puts("cd: #{arg}: not a directory\n") end + else do puts("cd: #{arg}: not a directory\n") shell_status.code = 1 end end fn cmd_pwd(context) puts("#{wd.path}\n") @@ -309,7 +309,7 @@ fn cmd_run(context) do if context.argv:len() <= 1 do list_progs() return :nil end const target = context.argv[1] const info = exec_file(target) - if info.kind == :none do puts("run: #{target}: not found\n") return :nil end + if info.kind == :none do fail("run: #{target}: not found\n") return :nil end const sub_argv = context.rest:split(" ") const sub_ctx = make_ctx(context.rest, sub_argv) if info.kind == :script do @@ -317,7 +317,7 @@ fn cmd_run(context) do return :nil end if load_file_elf(info.path, sub_ctx.rest) == :true return :hold - puts("run: #{target}: not executable\n") + fail("run: #{target}: not executable\n") return :nil end @@ -328,6 +328,7 @@ fn run(line) do if line:trim():len() == 0 do puts(prompt()) return :nil end const argv = line:split(" ") const context = make_ctx(line, argv) + shell_status.code = 0 const command = lookup(argv[0]) if command != :nil do # :hold means the command took over (modal editor / launched a program) so @@ -339,6 +340,7 @@ fn run(line) do if run_script(argv[0], context) == :true do puts(prompt()) return :nil end if run_prog(argv[0], context.rest) == :true return :nil + shell_status.code = 127 puts("#{argv[0]}: command not found\n") puts(prompt()) end |
