summaryrefslogtreecommitdiffstats
path: root/shell/src
diff options
context:
space:
mode:
Diffstat (limited to 'shell/src')
-rw-r--r--shell/src/lib.rs222
1 files changed, 220 insertions, 2 deletions
diff --git a/shell/src/lib.rs b/shell/src/lib.rs
index 500eaae..7e1439e 100644
--- a/shell/src/lib.rs
+++ b/shell/src/lib.rs
@@ -2,6 +2,8 @@
2 2
3extern crate alloc; 3extern crate alloc;
4 4
5use alloc::string::ToString;
6
5/// Minimal command shell — reads from PS/2 keyboard, writes to GOP + serial. 7/// Minimal command shell — reads from PS/2 keyboard, writes to GOP + serial.
6use rsnix::ps2::{self, KeyEvent}; 8use rsnix::ps2::{self, KeyEvent};
7use rsnix::{gop_print, gop_println, serial_print, serial_println}; 9use rsnix::{gop_print, gop_println, serial_print, serial_println};
@@ -52,6 +54,12 @@ pub fn run() -> ! {
52 "stress" => cmd_stress(args), 54 "stress" => cmd_stress(args),
53 "date" => cmd_date(), 55 "date" => cmd_date(),
54 "pci" => cmd_pci(), 56 "pci" => cmd_pci(),
57 "ls" => cmd_ls(args),
58 "cat" => cmd_cat(args),
59 "hexdump" => cmd_hexdump(args),
60 "mkdir" => cmd_mkdir(args),
61 "touch" => cmd_touch(args),
62 "rm" => cmd_rm(args),
55 _ => { 63 _ => {
56 gop_println!("unknown command: {}", cmd); 64 gop_println!("unknown command: {}", cmd);
57 serial_println!("unknown command: {}", cmd); 65 serial_println!("unknown command: {}", cmd);
@@ -128,15 +136,220 @@ fn cmd_help() {
128 gop_println!(" stress — stress-test heap/page allocators"); 136 gop_println!(" stress — stress-test heap/page allocators");
129 gop_println!(" date — show current date/time (RTC)"); 137 gop_println!(" date — show current date/time (RTC)");
130 gop_println!(" pci — list PCI devices"); 138 gop_println!(" pci — list PCI devices");
139 gop_println!(" ls — list a directory");
140 gop_println!(" cat — print files (e.g. cat /dev/rtc)");
141 gop_println!(" hexdump — hex-dump a file (e.g. hexdump /dev/mem)");
142 gop_println!(" mkdir — create a directory");
143 gop_println!(" touch — create an empty file");
144 gop_println!(" rm — remove a file or empty dir");
145 gop_println!(" echo > /dev/null, > file, >> file");
131 gop_println!(" halt — halt the system (hlt loop)"); 146 gop_println!(" halt — halt the system (hlt loop)");
132 gop_println!(" poweroff — shut down via ACPI (ResetSystem)"); 147 gop_println!(" poweroff — shut down via ACPI (ResetSystem)");
133 gop_println!(" exit — alias for poweroff"); 148 gop_println!(" exit — alias for poweroff");
134 serial_println!("available commands:"); 149 serial_println!("available commands:");
135 serial_println!(" help, echo, clear, hello, info, mem, mmap, stress, date, pci, halt, poweroff, exit"); 150 serial_println!(
151 " help, echo, clear, hello, info, mem, mmap, stress, date, pci, ls, cat, hexdump, mkdir, touch, rm, halt, poweroff, exit"
152 );
136} 153}
137 154
138fn cmd_echo(args: &str) { 155fn cmd_echo(args: &str) {
156 // Redirection: `text > path` (overwrite) or `text >> path` (append).
157 if let Some(idx) = args.find('>') {
158 let append = args[idx..].starts_with(">>");
159 let text = &args[..idx];
160 let path = args[idx + 1 + append as usize..].trim();
161 let path = path.split_whitespace().next().unwrap_or("");
162 if !path.is_empty() {
163 let content = alloc::format!("{}\n", text.trim_end());
164 let result = rsnix::fs::ensure_file(path).and_then(|h| {
165 if append {
166 rsnix::fs::write(h, content.as_bytes(), rsnix::fs::size(h))
167 .map(|_| ())
168 } else {
169 let _ = rsnix::fs::set_len(h, 0);
170 rsnix::fs::write(h, content.as_bytes(), 0).map(|_| ())
171 }
172 });
173 match result {
174 Ok(()) => {}
175 Err(()) => {
176 gop_println!("echo: cannot write '{}'", path);
177 serial_println!("echo: cannot write '{}'", path);
178 }
179 }
180 return;
181 }
182 }
139 gop_println!("{}", args); 183 gop_println!("{}", args);
184 serial_println!("{}", args);
185}
186
187fn cmd_ls(args: &str) {
188 let path = args.split_whitespace().next().unwrap_or("/");
189 let mut out = alloc::string::String::new();
190 match rsnix::fs::resolve(path) {
191 None => {
192 out.push_str(&alloc::format!("ls: {}: no such file or directory\n", path));
193 }
194 Some(h) => {
195 let list = if rsnix::fs::kind(h) == rsnix::fs::FileType::Dir {
196 rsnix::fs::children(h)
197 } else {
198 alloc::vec![(
199 path.rsplit('/').next().unwrap_or(path).to_string(),
200 rsnix::fs::kind(h),
201 rsnix::fs::size(h)
202 )]
203 };
204 for (name, kind, size) in list {
205 match kind {
206 rsnix::fs::FileType::Dir => out.push_str(&alloc::format!("{}/\n", name)),
207 rsnix::fs::FileType::Char => {
208 out.push_str(&alloc::format!("{}@\n", name))
209 }
210 rsnix::fs::FileType::Regular => {
211 out.push_str(&alloc::format!("{} {} B\n", name, size))
212 }
213 }
214 }
215 }
216 }
217 gop_print!("{}", out);
218 serial_print!("{}", out);
219}
220
221fn cmd_cat(args: &str) {
222 let paths: alloc::vec::Vec<&str> = args.split_whitespace().collect();
223 if paths.is_empty() {
224 gop_println!("usage: cat <path> [path...]");
225 serial_println!("usage: cat <path> [path...]");
226 return;
227 }
228 let mut out = alloc::string::String::new();
229 let mut buf = [0u8; 512];
230 for p in paths {
231 match rsnix::fs::resolve(p) {
232 None => out.push_str(&alloc::format!("cat: {}: no such file\n", p)),
233 Some(h) => {
234 let mut pos = 0u64;
235 loop {
236 match rsnix::fs::read(h, &mut buf, pos) {
237 Ok(0) | Err(_) => break,
238 Ok(n) => {
239 out.push_str(core::str::from_utf8(&buf[..n]).unwrap_or("?"));
240 pos += n as u64;
241 }
242 }
243 }
244 }
245 }
246 }
247 gop_print!("{}", out);
248 serial_print!("{}", out);
249}
250
251fn cmd_hexdump(args: &str) {
252 let path = args.split_whitespace().next().unwrap_or("");
253 if path.is_empty() {
254 gop_println!("usage: hexdump <path>");
255 serial_println!("usage: hexdump <path>");
256 return;
257 }
258 let h = match rsnix::fs::resolve(path) {
259 Some(h) => h,
260 None => {
261 gop_println!("hexdump: {}: no such file", path);
262 serial_println!("hexdump: {}: no such file", path);
263 return;
264 }
265 };
266 let mut out = alloc::string::String::new();
267 let mut buf = [0u8; 512];
268 let mut pos = 0u64;
269 use core::fmt::Write as _;
270 'outer: loop {
271 let n = match rsnix::fs::read(h, &mut buf, pos) {
272 Ok(0) | Err(_) => break,
273 Ok(n) => n,
274 };
275 for i in (0..n).step_by(16) {
276 let _ = core::write!(out, "{:08x} ", pos + i as u64);
277 for j in 0..16 {
278 if i + j < n {
279 let _ = core::write!(out, "{:02x} ", buf[i + j]);
280 } else {
281 out.push_str(" ");
282 }
283 }
284 out.push_str(" |");
285 for j in 0..16 {
286 if i + j >= n {
287 break;
288 }
289 let b = buf[i + j];
290 out.push(if b.is_ascii_graphic() || b == b' ' {
291 b as char
292 } else {
293 '.'
294 });
295 }
296 out.push_str("|\n");
297 if i + 16 >= n {
298 pos += n as u64;
299 continue 'outer;
300 }
301 }
302 }
303 gop_print!("{}", out);
304 serial_print!("{}", out);
305}
306
307fn cmd_mkdir(args: &str) {
308 let path = args.split_whitespace().next().unwrap_or("");
309 if path.is_empty() {
310 gop_println!("usage: mkdir <path>");
311 serial_println!("usage: mkdir <path>");
312 return;
313 }
314 match rsnix::fs::mkdir(path) {
315 Ok(()) => {}
316 Err(()) => {
317 gop_println!("mkdir: cannot create directory '{}'", path);
318 serial_println!("mkdir: cannot create directory '{}'", path);
319 }
320 }
321}
322
323fn cmd_touch(args: &str) {
324 let path = args.split_whitespace().next().unwrap_or("");
325 if path.is_empty() {
326 gop_println!("usage: touch <path>");
327 serial_println!("usage: touch <path>");
328 return;
329 }
330 match rsnix::fs::touch(path) {
331 Ok(()) => {}
332 Err(()) => {
333 gop_println!("touch: cannot create '{}'", path);
334 serial_println!("touch: cannot create '{}'", path);
335 }
336 }
337}
338
339fn cmd_rm(args: &str) {
340 let path = args.split_whitespace().next().unwrap_or("");
341 if path.is_empty() {
342 gop_println!("usage: rm <path>");
343 serial_println!("usage: rm <path>");
344 return;
345 }
346 match rsnix::fs::remove_path(path) {
347 Ok(()) => {}
348 Err(()) => {
349 gop_println!("rm: cannot remove '{}' (missing, dir not empty, or busy)", path);
350 serial_println!("rm: cannot remove '{}' (missing, dir not empty, or busy)", path);
351 }
352 }
140} 353}
141 354
142fn cmd_clear() { 355fn cmd_clear() {
@@ -372,7 +585,12 @@ fn cmd_date() {
372 let t = rsnix::rtc::now(); 585 let t = rsnix::rtc::now();
373 let line = alloc::format!( 586 let line = alloc::format!(
374 "{}-{:02}-{:02} {:02}:{:02}:{:02}", 587 "{}-{:02}-{:02} {:02}:{:02}:{:02}",
375 t.year, t.month, t.day, t.hour, t.minute, t.second 588 t.year,
589 t.month,
590 t.day,
591 t.hour,
592 t.minute,
593 t.second
376 ); 594 );
377 gop_println!("{}", line); 595 gop_println!("{}", line);
378 serial_println!("{}", line); 596 serial_println!("{}", line);