foundry
Foundry OS: the operating system built on the Rustbelt kernel
| author | Tyler Fowler <tyler@localhost> | 2026-10-30 01:32:12 -0700 |
|---|---|---|
| commit | 642316995676e40b82513422d30d6645005ca7fd | |
| parent | b10b990e384b578fd41000614e51b20a726c3103 | |
| tree | browse files at this commit | |
Shell: drive letters start at A: (root) instead of C: / is A:, mounted disks get B:, C:, ... in mount order (the FAT disk is B:). C: for the system drive is a floppy-era leftover; Rustbelt has no floppies. Updated shparse, sh help text, selftest.sh, disk readme, tests, scripts, README and boot.png.
| M | sh.rs | +3 -3 |
| M | shparse.rs | +12 -12 |
2 files changed, 15 insertions, 15 deletions
@@ -41,7 +41,7 @@ Rustbelt shell commands run [--trace] prog args run a program (--trace prints its syscalls) start prog args run it in the background (or: prog args &) a | b | c pipes; > file >> file < file redirection- C: is /, D: E: ... are the disks (see `drives`): cd D:, cat D:/readme.txt+ A: is /, B: C: ... are the disks (see `drives`): cd B:, cat B:/readme.txt "; /// A background job started with `start` or `&`.@@ -137,7 +137,7 @@ impl Shell { String::from_utf8_lossy(&buf[..n]).into_owned() } - /// `C:/ $ `, `D:/notes $ `+ /// `A:/ $ `, `B:/notes $ ` fn prompt(&self) -> String { format!("{} $ ", shparse::display(&self.cwd(), &self.drives)) }@@ -381,7 +381,7 @@ impl Shell { } /// Find a program: names with a `/` are paths; others are looked up in- /// the `bin` directory of every drive (our PATH): C:/bin, D:/bin, ...+ /// the `bin` directory of every drive (our PATH): A:/bin, B:/bin, ... fn find_program(&self, name: &str) -> Option<String> { if name.contains('/') { return Some(name.to_string());
@@ -1,5 +1,5 @@ //! The shell's parser: command line -> tokens -> pipeline, and the drive-//! letter mapping (`D:/notes` <-> `/mnt/disk0/notes`).+//! letter mapping (`B:/notes` <-> `/mnt/disk0/notes`). //! //! Pure functions with no syscalls, so the same file is compiled into the //! shell (user/sh.rs) *and* into the kernel's test suite (tests/shell.rs),@@ -20,7 +20,7 @@ use alloc::{format, string::String, vec::Vec}; /// One word of a command. `quoted` words are never drive-translated, so-/// `grep "C:" file` searches for the text C:.+/// `grep "A:" file` searches for the text A:. #[derive(Debug, Clone, PartialEq)] pub struct Word { pub text: String,@@ -161,17 +161,17 @@ pub struct Drive { /// Assign letters from the kernel's mount table (the text from the /// `mounts` syscall: one "path fstype" per line, in mount order).-/// C: is always the root; D:, E:, ... go to the mounts under /mnt (the+/// A: is always the root; B:, C:, ... go to the mounts under /mnt (the /// disks), in the order they were mounted. Other mounts such as /tmp are-/// reachable through C: (C:/tmp).+/// reachable through A: (A:/tmp). pub fn assign_drives(mount_table: &str) -> Vec<Drive> { let mut drives = Vec::new(); let root_fs = mount_table .lines() .find_map(|l| l.split_once(' ').filter(|(p, _)| *p == "/").map(|(_, fs)| fs)) .unwrap_or("ramfs");- drives.push(Drive { letter: 'C', path: String::from("/"), fs: String::from(root_fs.trim()) });- let mut letter = b'D';+ drives.push(Drive { letter: 'A', path: String::from("/"), fs: String::from(root_fs.trim()) });+ let mut letter = b'B'; for line in mount_table.lines() { if let Some((path, fs)) = line.split_once(' ') { if path.starts_with("/mnt/") && letter <= b'Z' {@@ -183,7 +183,7 @@ pub fn assign_drives(mount_table: &str) -> Vec<Drive> { drives } -/// Does `word` start with a drive letter ("D:", "d:/x", "E:notes")?+/// Does `word` start with a drive letter ("B:", "b:/x", "C:notes")? pub fn drive_letter(word: &str) -> Option<char> { let b = word.as_bytes(); if b.len() >= 2 && b[0].is_ascii_alphabetic() && b[1] == b':' {@@ -193,9 +193,9 @@ pub fn drive_letter(word: &str) -> Option<char> { } } -/// `D:/notes/todo.txt` -> `/mnt/disk0/notes/todo.txt`. Letters are case-/// insensitive; `D:` and `D:/` are the drive's root, and `D:notes` means-/// `D:/notes` (drives have no current directory of their own). `None` if+/// `B:/notes/todo.txt` -> `/mnt/disk0/notes/todo.txt`. Letters are case+/// insensitive; `B:` and `B:/` are the drive's root, and `B:notes` means+/// `B:/notes` (drives have no current directory of their own). `None` if /// `word` doesn't start with a known drive letter. pub fn translate(word: &str, drives: &[Drive]) -> Option<String> { let letter = drive_letter(word)?;@@ -210,8 +210,8 @@ pub fn translate(word: &str, drives: &[Drive]) -> Option<String> { }) } -/// The other direction, for the prompt: `/mnt/disk0/bin` -> `D:/bin`,-/// `/tmp` -> `C:/tmp`. Picks the drive with the longest matching path.+/// The other direction, for the prompt: `/mnt/disk0/bin` -> `B:/bin`,+/// `/tmp` -> `A:/tmp`. Picks the drive with the longest matching path. pub fn display(path: &str, drives: &[Drive]) -> String { let mut best: Option<(&Drive, &str)> = None; for d in drives {