moonshot

technical

skalman

skalman is moonshot's command line.the name contains "skal", Swedish for shell. it reads a line of keystrokes, echoes them to the screen through punkt, and on Enter runs the named command. its line buffer was the kernel heap's first real customer, a single allocation from jenna's kmalloc.

since kakel arrived there is one shell session per window, each with its own line buffer and its own prompt. keystrokes go to the focused window's session, a half-typed command survives switching focus away and back, and a command's output lands in the window it was typed in.focus cannot change in the middle of a command, the keystroke that runs it and the command itself happen inside one keyboard interrupt, and interrupts do not nest.

where it runs

skalman is a scheduled task, one of chrone's own. the keyboard handler echoes each key to the screen through kakel, then deposits the decoded character into an input ring buffer and wakes the shell task. the shell task blocks on an empty queue (the first use of TASK_BLOCKED and task_wake) and when woken it dequeues one character at a time and feeds it to shell_input.

for command history the keyboard handler detects up and down arrow keys and calls into skalman's history ring buffer directly from the irq, clearing the current line and redrawing the recalled command. this is safe because interrupt gates keep the keyboard and timer handlers from nesting.

for line editing the left and right arrow keys move the cursor within the current line. typing with the cursor mid-line inserts characters at that position (shifting the rest right) and backspace deletes the character before the cursor and shifts the remainder left. a quick line redraw fixes the visual state after mid-line edits (the keyboard handler's echo path assumes append mode). this covers the common case of fixing a typo without erasing the whole command. home and end keys (or ctrl-a / ctrl-e) jump to the start or end of the line. ctrl-w deletes the word before the cursor and ctrl-k kills everything from the cursor to the end of the line.

command reference

the command set grows when a piece of the kernel is worth poking at from the keyboard. type help for the current list, or help (command) for details. arguments are written in parentheses because that is how the shell itself writes them.

help (command)
shows the full command list, or details about one command.
clear
clears the focused window.
chars
prints a grid of all printable ascii glyphs.
echo (text)
prints its argument back. echo --file (path) prints a file's contents.
list (path)
shows the contents of a directory, with sizes and permissions.
change (path)
changes the current working directory.
make (name)
creates an empty file (name with a dot) or directory.
remove (name)
deletes a file or empty directory.
write (name) (text)
writes text into a file, creating it if needed.
copy (src) (dst)
copies a file. both paths may contain slashes.
rename (src) (dst)
renames or moves a file or directory across directories.
count (file)
prints lines, words, and bytes in a file.
where
prints the current working directory path.
sync
persists all files to disk.
mode (path) (level)
sets file permissions to public, private, or locked.
own (path) (user)
changes a file's owner.
whoami
prints the current user name.
login (name)
switches user for this session, with optional password.
passwd (user) (password)
sets or changes a user's password.
adduser (name)
creates a new user. the user table is fixed-size.
eval (expr)
evaluates a c0 expression through the repl. eval -c uses the jit compiler.
run (file)
executes a file line-by-line through the repl interpreter.
edit (file)
opens a file in the skrift text editor.
tasks
lists every task in the scheduler table.
spawn
starts a new worker task.
kill (id)
terminates a task by its id.
run3 / spawn3
drops to ring 3 and runs a test binary. run3 does it in the current task (the shell is lost when the process exits). spawn3 creates a new task with its own kakel window. spawn3 (file) loads and runs a real ELF64 executable from jakel and run3 (file) does the same in the current task.
split
adds a new window to the grid. the new window inherits the current session's authenticated user and working directory, the login prompt is skipped.
close
removes the focused window. closing a tavla also stops the graphical program behind it, since the window is the only place it had to draw. alt+w does the same from the keyboard, and is the way out of a tavla that is holding every keystroke.

anything else prints unknown command: followed by what was typed. an empty line just prompts again. while typing, backspace erases, and a line is capped at 127 characters.

tab completion

press tab to complete commands and filenames. the shell looks at the word before the cursor. if it is the first word on the line, it completes against the builtin command names. if it is a later word (after a space), it completes against filenames in the current working directory through jakel.

a single match completes the word fully, with a trailing space for commands so you can type the next argument immediately. multiple matches complete the longest common prefix. if the common prefix is already fully expanded and there are still multiple candidates, pressing tab a second time lists them all below the prompt, the same behaviour as a modern shell.

quoting

arguments can be wrapped in double quotes to include spaces. write "my file.txt" hello world writes "hello world" to a file named "my file.txt". the shell_next_arg helper handles both quoted and unquoted arguments, available to all builtins.

repl

the eval (expr) builtin parses and evaluates c0 statements at runtime via a recursive tree-walking interpreter. it supports integer literals (decimal and hex), arithmetic with correct precedence, comparisons, unary minus, session-persistent variables, and a print(expr) builtin. control flow covers if/else, while, for loops (all three parts optional), break and continue, and { } blocks with semicolon-separated statements.

eval -c (expr) compiles and runs c0 natively via the jit compiler in jit.c0. it walks the same ast the interpreter uses, emits raw x86-64 machine code into an identity-mapped page, and invokes it through c0's indirect-call mechanism. the compiled code runs with full W^X page protection, the page is writable during compilation and executable during execution, never both.

the jit now handles the full statement set, meaning arithmetic and comparisons, if/else, while and for loops with break and continue, { } blocks, variable assignment, print() with both string and integer arguments, and builtin calls like load8(addr), load64(addr), store8(addr, val), and store64(addr, val). string literals are embedded directly in the code page. multi-statement programs compile and run as a single native invocation. type help eval for details.

run (file) reads a file from jakel and executes it line-by-line through the repl interpreter -- the first batch-execution primitive. on first boot a small demo.c0 is seeded in jakel, type run demo.c0 to try it.

editor

the edit (filename) builtin opens the named file in skrift, the text editor. the editor takes over the window -- typing edits the buffer, escape enters command mode, arrow keys move the cursor, q quits and saves. the file is read from and written to jakel. saving does not sync to disk, run sync after quitting to persist. type help edit for details.

users and permissions

skalman has a simple multi-user system. adduser (name) creates a new user in a fixed-size table. login (name) [password] switches the current session to that user. whoami prints the current user name. root (uid 0) and guest (uid 1) are seeded at boot.

passwd (user) (password) sets a plaintext password for a user. once a password is set, login (name) requires it. the session stays as the current user if the password is wrong. passwords are stored as copies allocated with kmalloc on the kernel heap.

file ownership and permissions are managed through jakel. own (path) (user) changes a file's owner. mode (path) public|private|locked sets the permission level.

path-aware copy and rename

copy (src) (dst) and rename (src) (dst) both support destination paths with slashes. copy readme.txt docs/readme.txt copies into the docs directory (which must already exist). rename old.txt archive/old.txt moves a file across directories by reparenting the node, the file keeps its data and only its name and parent pointer change.