diff --git a/doc/shell.md b/doc/shell.md index bb689a6c4..aea435962 100644 --- a/doc/shell.md +++ b/doc/shell.md @@ -130,6 +130,7 @@ Which is more efficient than doing: - Name of the shell or the script: `$0` - Script arguments: `$1`, `$2`, `$3`, `$4`, ... +- Exit code: `$?` - Process environment variable: `$HOME`, ... - Shell environment variable: `$foo`, ... diff --git a/dsk/bin/clear b/dsk/bin/clear index 80664c3c1..e012142a0 100644 Binary files a/dsk/bin/clear and b/dsk/bin/clear differ diff --git a/dsk/bin/halt b/dsk/bin/halt index 8abda2982..649efcd44 100644 Binary files a/dsk/bin/halt and b/dsk/bin/halt differ diff --git a/dsk/bin/hello b/dsk/bin/hello index 60827a88f..c81bd7dee 100755 Binary files a/dsk/bin/hello and b/dsk/bin/hello differ diff --git a/dsk/bin/print b/dsk/bin/print index 3cc9af181..c07989537 100644 Binary files a/dsk/bin/print and b/dsk/bin/print differ diff --git a/dsk/bin/reboot b/dsk/bin/reboot index 23841eb7b..9337292fc 100644 Binary files a/dsk/bin/reboot and b/dsk/bin/reboot differ diff --git a/dsk/bin/sleep b/dsk/bin/sleep index 331841144..4a5e17859 100644 Binary files a/dsk/bin/sleep and b/dsk/bin/sleep differ diff --git a/src/api/mod.rs b/src/api/mod.rs index a54146bd7..796e1ce15 100644 --- a/src/api/mod.rs +++ b/src/api/mod.rs @@ -11,9 +11,9 @@ macro_rules! entry_point { #[export_name = "_start"] pub unsafe extern "sysv64" fn __impl_start(args_ptr: u64, args_len: usize) { let args = core::slice::from_raw_parts(args_ptr as *const _, args_len); - let f: fn(&[&str]) -> usize = $path; - let code = f(args); - $crate::api::syscall::exit(code); + let f: fn(&[&str]) = $path; + f(args); + $crate::api::syscall::exit($crate::api::process::ExitCode::Success); } }; } diff --git a/src/api/process.rs b/src/api/process.rs index 20f89d22a..09df09116 100644 --- a/src/api/process.rs +++ b/src/api/process.rs @@ -1,8 +1,39 @@ use crate::api::syscall; -pub fn spawn(path: &str, args: &[&str]) -> Result<(), ()> { +use core::convert::From; + +#[derive(Copy, Clone, PartialEq, Eq)] +#[repr(u8)] +pub enum ExitCode { + Success = 0, + Failure = 1, + UsageError = 64, + DataError = 65, + OpenError = 128, + ReadError = 129, + ExecError = 130, + ShellExit = 255, +} + +impl From for ExitCode { + fn from(code: usize) -> Self { + match code { + 0 => ExitCode::Success, + 64 => ExitCode::UsageError, + 65 => ExitCode::DataError, + 128 => ExitCode::OpenError, + 129 => ExitCode::ReadError, + 130 => ExitCode::ExecError, + 255 => ExitCode::ShellExit, + _ => ExitCode::Failure, + } + } +} + +pub fn spawn(path: &str, args: &[&str]) -> Result<(), ExitCode> { if syscall::info(path).is_some() { - return syscall::spawn(path, args); + syscall::spawn(path, args) + } else { + Err(ExitCode::OpenError) } - Err(()) } diff --git a/src/api/syscall.rs b/src/api/syscall.rs index 0dcaae092..f96350840 100644 --- a/src/api/syscall.rs +++ b/src/api/syscall.rs @@ -1,9 +1,10 @@ +use crate::api::process::ExitCode; use crate::syscall; use crate::sys::syscall::number::*; use crate::sys::fs::FileInfo; -pub fn exit(code: usize) -> usize { - unsafe { syscall!(EXIT, code as u64) } +pub fn exit(code: ExitCode) { + unsafe { syscall!(EXIT, code as usize) }; } pub fn sleep(seconds: f64) { @@ -14,10 +15,10 @@ pub fn delete(path: &str) -> Result<(), ()> { let path_ptr = path.as_ptr() as usize; let path_len = path.len() as usize; let res = unsafe { syscall!(DELETE, path_ptr, path_len) } as isize; - if res.is_negative() { - Err(()) - } else { + if res >= 0 { Ok(()) + } else { + Err(()) } } @@ -27,10 +28,10 @@ pub fn info(path: &str) -> Option { let mut info = FileInfo::new(); let stat_ptr = &mut info as *mut FileInfo as usize; let res = unsafe { syscall!(INFO, path_ptr, path_len, stat_ptr) } as isize; - if res.is_negative() { - None - } else { + if res >= 0 { Some(info) + } else { + None } } @@ -38,19 +39,19 @@ pub fn open(path: &str, flags: usize) -> Option { let ptr = path.as_ptr() as usize; let len = path.len() as usize; let res = unsafe { syscall!(OPEN, ptr, len, flags) } as isize; - if res.is_negative() { - None - } else { + if res >= 0 { Some(res as usize) + } else { + None } } pub fn dup(old_handle: usize, new_handle: usize) -> Option { let res = unsafe { syscall!(DUP, old_handle, new_handle) } as isize; - if res.is_negative() { - None - } else { + if res >= 0 { Some(res as usize) + } else { + None } } @@ -58,10 +59,10 @@ pub fn read(handle: usize, buf: &mut [u8]) -> Option { let ptr = buf.as_ptr() as usize; let len = buf.len() as usize; let res = unsafe { syscall!(READ, handle, ptr, len) } as isize; - if res.is_negative() { - None - } else { + if res >= 0 { Some(res as usize) + } else { + None } } @@ -69,10 +70,10 @@ pub fn write(handle: usize, buf: &[u8]) -> Option { let ptr = buf.as_ptr() as usize; let len = buf.len() as usize; let res = unsafe { syscall!(WRITE, handle, ptr, len) } as isize; - if res.is_negative() { - None - } else { + if res >= 0 { Some(res as usize) + } else { + None } } @@ -80,16 +81,16 @@ pub fn close(handle: usize) { unsafe { syscall!(CLOSE, handle as usize) }; } -pub fn spawn(path: &str, args: &[&str]) -> Result<(), ()> { +pub fn spawn(path: &str, args: &[&str]) -> Result<(), ExitCode> { let path_ptr = path.as_ptr() as usize; let path_len = path.len() as usize; let args_ptr = args.as_ptr() as usize; let args_len = args.len() as usize; - let res = unsafe { syscall!(SPAWN, path_ptr, path_len, args_ptr, args_len) } as isize; - if res.is_negative() { - Err(()) - } else { + let res = unsafe { syscall!(SPAWN, path_ptr, path_len, args_ptr, args_len) }; + if res == 0 { Ok(()) + } else { + Err(ExitCode::from(res)) } } diff --git a/src/bin/clear.rs b/src/bin/clear.rs index 2e7d2fe3d..c1d9aa72a 100644 --- a/src/bin/clear.rs +++ b/src/bin/clear.rs @@ -6,7 +6,6 @@ use moros::entry_point; entry_point!(main); -fn main(_args: &[&str]) -> usize { +fn main(_args: &[&str]) { syscall::write(1, b"\x1b[2J\x1b[1;1H"); // Clear screen and move cursor to top - 0 } diff --git a/src/bin/halt.rs b/src/bin/halt.rs index 6a9900d0a..a6e4af9de 100644 --- a/src/bin/halt.rs +++ b/src/bin/halt.rs @@ -6,7 +6,7 @@ use moros::entry_point; entry_point!(main); -fn main(_args: &[&str]) -> usize { +fn main(_args: &[&str]) { syscall::write(1, b"\x1b[93m"); // Yellow syscall::write(1, b"MOROS has reached its fate, the system is now halting.\n"); syscall::write(1, b"\x1b[0m"); // Reset diff --git a/src/bin/hello.rs b/src/bin/hello.rs index 2a31ce16d..ddac4ba0c 100644 --- a/src/bin/hello.rs +++ b/src/bin/hello.rs @@ -6,7 +6,6 @@ use moros::entry_point; entry_point!(main); -fn main(_args: &[&str]) -> usize { +fn main(_args: &[&str]) { syscall::write(1, b"Hello, World!\n"); - 0 } diff --git a/src/bin/print.rs b/src/bin/print.rs index ccd377242..4483687f5 100644 --- a/src/bin/print.rs +++ b/src/bin/print.rs @@ -6,7 +6,7 @@ use moros::api::syscall; entry_point!(main); -fn main(args: &[&str]) -> usize { +fn main(args: &[&str]) { let n = args.len(); for i in 1..n { syscall::write(1, args[i].as_bytes()); @@ -15,5 +15,4 @@ fn main(args: &[&str]) -> usize { } } syscall::write(1, b"\n"); - 0 } diff --git a/src/bin/reboot.rs b/src/bin/reboot.rs index ef390e3b0..19f1f82af 100644 --- a/src/bin/reboot.rs +++ b/src/bin/reboot.rs @@ -6,7 +6,7 @@ use moros::entry_point; entry_point!(main); -fn main(_args: &[&str]) -> usize { +fn main(_args: &[&str]) { syscall::write(1, b"\x1b[93m"); // Yellow syscall::write(1, b"MOROS has reached its fate, the system is now rebooting.\n"); syscall::write(1, b"\x1b[0m"); // Reset diff --git a/src/bin/sleep.rs b/src/bin/sleep.rs index 49ca244bf..32ed6aca5 100644 --- a/src/bin/sleep.rs +++ b/src/bin/sleep.rs @@ -2,16 +2,20 @@ #![no_main] use moros::api::syscall; +use moros::api::process::ExitCode; use moros::entry_point; entry_point!(main); -fn main(args: &[&str]) -> usize { +fn main(args: &[&str]) { if args.len() == 2 { if let Ok(duration) = args[1].parse::() { syscall::sleep(duration); - return 0 + return; + } else { + syscall::exit(ExitCode::DataError); } + } else { + syscall::exit(ExitCode::UsageError); } - 1 } diff --git a/src/lib.rs b/src/lib.rs index 596b64a25..f8f8733a3 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -13,7 +13,9 @@ extern crate alloc; #[macro_use] pub mod api; +#[macro_use] pub mod sys; + pub mod usr; use bootloader::BootInfo; diff --git a/src/main.rs b/src/main.rs index 85622aed0..e64cca512 100644 --- a/src/main.rs +++ b/src/main.rs @@ -16,7 +16,7 @@ fn main(boot_info: &'static BootInfo) -> ! { if let Some(cmd) = option_env!("MOROS_CMD") { let prompt = usr::shell::prompt_string(true); println!("{}{}", prompt, cmd); - usr::shell::exec(cmd); + usr::shell::exec(cmd).ok(); sys::acpi::shutdown(); } else { user_boot(); @@ -27,7 +27,7 @@ fn main(boot_info: &'static BootInfo) -> ! { fn user_boot() { let script = "/ini/boot.sh"; if sys::fs::File::open(script).is_some() { - usr::shell::main(&["shell", script]); + usr::shell::main(&["shell", script]).ok(); } else { if sys::fs::is_mounted() { println!("Could not find '{}'", script); @@ -35,7 +35,7 @@ fn user_boot() { println!("MFS is not mounted to '/'"); } println!("Running console in diskless mode"); - usr::shell::main(&["shell"]); + usr::shell::main(&["shell"]).ok(); } } diff --git a/src/sys/process.rs b/src/sys/process.rs index 06f6015ce..a5c2f99e1 100644 --- a/src/sys/process.rs +++ b/src/sys/process.rs @@ -1,3 +1,4 @@ +use crate::api::process::ExitCode; use crate::sys::fs::{Resource, Device}; use crate::sys::console::Console; @@ -248,7 +249,7 @@ impl Process { } } - pub fn spawn(bin: &[u8], args_ptr: usize, args_len: usize) -> Result<(), ()> { + pub fn spawn(bin: &[u8], args_ptr: usize, args_len: usize) -> Result<(), ExitCode> { if let Ok(pid) = Self::create(bin) { let proc = { let table = PROCESS_TABLE.read(); @@ -257,7 +258,7 @@ impl Process { proc.exec(args_ptr, args_len); Ok(()) } else { - Err(()) + Err(ExitCode::ExecError) } } diff --git a/src/sys/syscall/mod.rs b/src/sys/syscall/mod.rs index 00f4313fb..b7f24b2ab 100644 --- a/src/sys/syscall/mod.rs +++ b/src/sys/syscall/mod.rs @@ -1,6 +1,7 @@ pub mod number; pub mod service; +use crate::api::process::ExitCode; use crate::sys; use crate::sys::fs::FileInfo; @@ -13,7 +14,7 @@ use core::arch::asm; pub fn dispatcher(n: usize, arg1: usize, arg2: usize, arg3: usize, arg4: usize) -> usize { match n { number::EXIT => { - service::exit(arg1) + service::exit(ExitCode::from(arg1)) as usize } number::SLEEP => { service::sleep(f64::from_bits(arg1 as u64)); diff --git a/src/sys/syscall/service.rs b/src/sys/syscall/service.rs index b1c7b6ec7..6c96bfd49 100644 --- a/src/sys/syscall/service.rs +++ b/src/sys/syscall/service.rs @@ -1,13 +1,15 @@ use crate::sys; +use crate::api::process::ExitCode; use crate::sys::fs::FileInfo; use crate::sys::fs::FileIO; use crate::sys::process::Process; + use alloc::vec; use core::arch::asm; -pub fn exit(_code: usize) -> usize { +pub fn exit(code: ExitCode) -> ExitCode { sys::process::exit(); - 0 + code } pub fn sleep(seconds: f64) { @@ -80,21 +82,26 @@ pub fn close(handle: usize) { sys::process::delete_file_handle(handle); } -pub fn spawn(path: &str, args_ptr: usize, args_len: usize) -> isize { +pub fn spawn(path: &str, args_ptr: usize, args_len: usize) -> ExitCode { let path = match sys::fs::canonicalize(path) { Ok(path) => path, - Err(_) => return -1, + Err(_) => return ExitCode::OpenError, }; if let Some(mut file) = sys::fs::File::open(&path) { let mut buf = vec![0; file.size()]; if let Ok(bytes) = file.read(&mut buf) { buf.resize(bytes, 0); - if Process::spawn(&buf, args_ptr, args_len).is_ok() { - return 0; + if let Err(code) = Process::spawn(&buf, args_ptr, args_len) { + code + } else { + ExitCode::Success } + } else { + ExitCode::ReadError } + } else { + ExitCode::OpenError } - -1 } pub fn stop(code: usize) -> usize { diff --git a/src/usr/base64.rs b/src/usr/base64.rs index 8b191091d..ad97a25d2 100644 --- a/src/usr/base64.rs +++ b/src/usr/base64.rs @@ -1,15 +1,16 @@ -use crate::usr; +use crate::api::process::ExitCode; + use alloc::string::String; use alloc::vec::Vec; -pub fn main(args: &[&str]) -> usr::shell::ExitCode { +pub fn main(args: &[&str]) -> Result<(), ExitCode> { if args.len() != 2 { - usr::shell::ExitCode::CommandError + Err(ExitCode::UsageError) } else { let buf = encode(args[1].as_bytes()); let encoded = String::from_utf8(buf).unwrap(); println!("{}", encoded); - usr::shell::ExitCode::CommandSuccessful + Ok(()) } } diff --git a/src/usr/beep.rs b/src/usr/beep.rs index 1a28b3330..6955e440c 100644 --- a/src/usr/beep.rs +++ b/src/usr/beep.rs @@ -1,4 +1,5 @@ -use crate::{api, sys, usr}; +use crate::{api, sys}; +use crate::api::process::ExitCode; use crate::api::console::Style; use x86_64::instructions::port::Port; @@ -31,7 +32,7 @@ fn beep(freq: f64, len: f64) { stop_sound(); } -pub fn main(args: &[&str]) -> usr::shell::ExitCode { +pub fn main(args: &[&str]) -> Result<(), ExitCode> { let mut freq = 440.0; let mut len = 200.0; let mut i = 1; @@ -47,12 +48,12 @@ pub fn main(args: &[&str]) -> usr::shell::ExitCode { freq = value; } else { error!("Could not parse freq"); - return usr::shell::ExitCode::CommandError; + return Err(ExitCode::Failure); } i += 1; } else { error!("Missing freq"); - return usr::shell::ExitCode::CommandError; + return Err(ExitCode::UsageError); } }, "-l" | "--len" => { @@ -61,12 +62,12 @@ pub fn main(args: &[&str]) -> usr::shell::ExitCode { len = value; } else { error!("Could not parse len"); - return usr::shell::ExitCode::CommandError; + return Err(ExitCode::Failure); } i += 1; } else { error!("Missing len"); - return usr::shell::ExitCode::CommandError; + return Err(ExitCode::UsageError); } }, _ => {}, @@ -75,10 +76,10 @@ pub fn main(args: &[&str]) -> usr::shell::ExitCode { } beep(freq, len / 1000.0); - usr::shell::ExitCode::CommandSuccessful + Ok(()) } -fn help() -> usr::shell::ExitCode { +fn help() -> Result<(), ExitCode> { let csi_option = Style::color("LightCyan"); let csi_title = Style::color("Yellow"); let csi_reset = Style::reset(); @@ -87,5 +88,5 @@ fn help() -> usr::shell::ExitCode { println!("{}Options:{}", csi_title, csi_reset); println!(" {0}-f{1},{0} --freq {1} Tone frequency", csi_option, csi_reset); println!(" {0}-l{1},{0} --len {1} Tone length", csi_option, csi_reset); - usr::shell::ExitCode::CommandSuccessful + Ok(()) } diff --git a/src/usr/calc.rs b/src/usr/calc.rs index 7698124b2..a6e354735 100644 --- a/src/usr/calc.rs +++ b/src/usr/calc.rs @@ -1,4 +1,4 @@ -use crate::usr; +use crate::api::process::ExitCode; use crate::api::prompt::Prompt; use crate::api::console::Style; @@ -106,7 +106,7 @@ fn parse_eval(line: &str) -> Result { } } -fn repl() -> usr::shell::ExitCode { +fn repl() -> Result<(), ExitCode> { println!("MOROS Calc v0.1.0\n"); let csi_color = Style::color("Cyan"); let csi_error = Style::color("LightRed"); @@ -118,7 +118,7 @@ fn repl() -> usr::shell::ExitCode { prompt.history.load(history_file); while let Some(line) = prompt.input(&prompt_string) { - if line == "quit" { + if line == "q" || line == "quit" { break; } if line.is_empty() { @@ -139,21 +139,21 @@ fn repl() -> usr::shell::ExitCode { prompt.history.add(&line); prompt.history.save(history_file); } - usr::shell::ExitCode::CommandSuccessful + Ok(()) } -pub fn main(args: &[&str]) -> usr::shell::ExitCode { +pub fn main(args: &[&str]) -> Result<(), ExitCode> { if args.len() == 1 { repl() } else { match parse_eval(&args[1..].join(" ")) { Ok(res) => { println!("{}", res); - usr::shell::ExitCode::CommandSuccessful + Ok(()) } Err(msg) => { error!("{}", msg); - usr::shell::ExitCode::CommandError + Err(ExitCode::Failure) } } } diff --git a/src/usr/chess.rs b/src/usr/chess.rs index 72d643dca..6e1b78d30 100644 --- a/src/usr/chess.rs +++ b/src/usr/chess.rs @@ -1,5 +1,6 @@ -use crate::{api, usr, sys}; +use crate::{api, sys}; use crate::api::console::Style; +use crate::api::process::ExitCode; use crate::api::prompt::Prompt; use alloc::format; @@ -286,8 +287,8 @@ fn is_move(m: &str) -> bool { false } -pub fn main(_args: &[&str]) -> usr::shell::ExitCode { +pub fn main(_args: &[&str]) -> Result<(), ExitCode> { let mut chess = Chess::new(); chess.play(); - usr::shell::ExitCode::CommandSuccessful + Ok(()) } diff --git a/src/usr/colors.rs b/src/usr/colors.rs index e61817d7b..f55b56a0a 100644 --- a/src/usr/colors.rs +++ b/src/usr/colors.rs @@ -1,7 +1,8 @@ -use crate::usr; +use crate::api::process::ExitCode; + use alloc::format; -pub fn main(_args: &[&str]) -> usr::shell::ExitCode { +pub fn main(_args: &[&str]) -> Result<(), ExitCode> { let csi_reset = "\x1b[0m"; for i in 30..38 { @@ -25,5 +26,5 @@ pub fn main(_args: &[&str]) -> usr::shell::ExitCode { } println!(); - usr::shell::ExitCode::CommandSuccessful + Ok(()) } diff --git a/src/usr/copy.rs b/src/usr/copy.rs index 4be00c0e5..c2993ed89 100644 --- a/src/usr/copy.rs +++ b/src/usr/copy.rs @@ -1,10 +1,10 @@ -use crate::usr; use crate::api::fs; +use crate::api::process::ExitCode; -pub fn main(args: &[&str]) -> usr::shell::ExitCode { +pub fn main(args: &[&str]) -> Result<(), ExitCode> { if args.len() != 3 { eprintln!("Usage: copy "); - return usr::shell::ExitCode::CommandError; + return Err(ExitCode::UsageError); } let source = args[1]; @@ -12,13 +12,13 @@ pub fn main(args: &[&str]) -> usr::shell::ExitCode { if let Ok(contents) = fs::read_to_bytes(source) { if fs::write(dest, &contents).is_ok() { - usr::shell::ExitCode::CommandSuccessful + Ok(()) } else { error!("Could not write to '{}'", dest); - usr::shell::ExitCode::CommandError + Err(ExitCode::Failure) } } else { error!("File not found '{}'", source); - usr::shell::ExitCode::CommandError + Err(ExitCode::Failure) } } diff --git a/src/usr/date.rs b/src/usr/date.rs index fb9724fd4..63c4f80fb 100644 --- a/src/usr/date.rs +++ b/src/usr/date.rs @@ -1,16 +1,17 @@ -use crate::{api, usr}; +use crate::api; +use crate::api::process::ExitCode; use time::validate_format_string; -pub fn main(args: &[&str]) -> usr::shell::ExitCode { +pub fn main(args: &[&str]) -> Result<(), ExitCode> { let format = if args.len() > 1 { args[1] } else { "%F %H:%M:%S" }; match validate_format_string(format) { Ok(()) => { println!("{}", api::time::now().format(format)); - usr::shell::ExitCode::CommandSuccessful + Ok(()) } Err(e) => { error!("{}", e); - usr::shell::ExitCode::CommandError + Err(ExitCode::Failure) } } } diff --git a/src/usr/delete.rs b/src/usr/delete.rs index 6472bcb1f..1562c1f67 100644 --- a/src/usr/delete.rs +++ b/src/usr/delete.rs @@ -1,10 +1,10 @@ -use crate::usr; +use crate::api::process::ExitCode; use crate::api::syscall; use crate::api::fs; -pub fn main(args: &[&str]) -> usr::shell::ExitCode { +pub fn main(args: &[&str]) -> Result<(), ExitCode> { if args.len() < 2 { - return usr::shell::ExitCode::CommandError; + return Err(ExitCode::UsageError); } for arg in &args[1..] { @@ -18,20 +18,20 @@ pub fn main(args: &[&str]) -> usr::shell::ExitCode { if !fs::exists(pathname) { error!("File not found '{}'", pathname); - return usr::shell::ExitCode::CommandError; + return Err(ExitCode::Failure); } if let Some(info) = syscall::info(pathname) { if info.is_dir() && info.size() > 0 { error!("Directory '{}' not empty", pathname); - return usr::shell::ExitCode::CommandError; + return Err(ExitCode::Failure); } } if fs::delete(pathname).is_err() { error!("Could not delete file '{}'", pathname); - return usr::shell::ExitCode::CommandError; + return Err(ExitCode::Failure); } } - usr::shell::ExitCode::CommandSuccessful + Ok(()) } diff --git a/src/usr/dhcp.rs b/src/usr/dhcp.rs index 76925ddb6..d3b83d33e 100644 --- a/src/usr/dhcp.rs +++ b/src/usr/dhcp.rs @@ -1,12 +1,15 @@ use crate::{sys, usr, debug}; use crate::api::clock; +use crate::api::process::ExitCode; use crate::api::syscall; + +use alloc::format; use alloc::string::ToString; use alloc::vec::Vec; use smoltcp::socket::{Dhcpv4Event, Dhcpv4Socket}; use smoltcp::time::Instant; -pub fn main(args: &[&str]) -> usr::shell::ExitCode { +pub fn main(args: &[&str]) -> Result<(), ExitCode> { let mut verbose = false; let dhcp_config; @@ -31,12 +34,12 @@ pub fn main(args: &[&str]) -> usr::shell::ExitCode { if clock::realtime() - started > timeout { error!("Timeout reached"); iface.remove_socket(dhcp_handle); - return usr::shell::ExitCode::CommandError; + return Err(ExitCode::Failure); } if sys::console::end_of_text() || sys::console::end_of_transmission() { eprintln!(); iface.remove_socket(dhcp_handle); - return usr::shell::ExitCode::CommandError; + return Err(ExitCode::Failure); } let timestamp = Instant::from_micros((clock::realtime() * 1000000.0) as i64); @@ -65,29 +68,29 @@ pub fn main(args: &[&str]) -> usr::shell::ExitCode { } } else { error!("Network Error"); - return usr::shell::ExitCode::CommandError; + return Err(ExitCode::Failure); } if let Some(config) = dhcp_config { //debug!("{:#?}", config); - usr::net::main(&["net", "config", "ip", &config.address.to_string()]); - usr::net::main(&["net", "config", "ip"]); + usr::shell::exec(&format!("net config ip {}", config.address)).ok(); + usr::shell::exec("net config ip").ok(); if let Some(router) = config.router { - usr::net::main(&["net", "config", "gw", &router.to_string()]); + usr::shell::exec(&format!("net config gw {}", router)).ok(); } else { - usr::net::main(&["net", "config", "gw", "0.0.0.0"]); + usr::shell::exec("net config gw 0.0.0.0").ok(); } - usr::net::main(&["net", "config", "gw"]); + usr::shell::exec("net config gw").ok(); let dns: Vec<_> = config.dns_servers.iter().filter_map(|s| *s).map(|s| s.to_string()).collect(); if !dns.is_empty() { - usr::net::main(&["net", "config", "dns", &dns.join(",")]); + usr::shell::exec(&format!("net config dns {}", dns.join(","))).ok(); } - usr::net::main(&["net", "config", "dns"]); + usr::shell::exec("net config dns").ok(); - return usr::shell::ExitCode::CommandSuccessful; + return Ok(()); } - usr::shell::ExitCode::CommandError + Err(ExitCode::Failure) } diff --git a/src/usr/disk.rs b/src/usr/disk.rs index 0c2fae6dc..c5e45b7ad 100644 --- a/src/usr/disk.rs +++ b/src/usr/disk.rs @@ -1,6 +1,7 @@ -use crate::{sys, usr}; use crate::api::console::Style; use crate::api::io; +use crate::api::process::ExitCode; +use crate::sys; use crate::sys::ata::Drive; use alloc::format; @@ -9,7 +10,7 @@ use alloc::string::ToString; use alloc::vec::Vec; use alloc::vec; -pub fn main(args: &[&str]) -> usr::shell::ExitCode { +pub fn main(args: &[&str]) -> Result<(), ExitCode> { if args.len() == 1 { return usage(); } @@ -32,23 +33,23 @@ fn parse_disk_path(pathname: &str) -> Result<(u8, u8), String> { Ok((bus, dsk)) } -fn format(pathname: &str) -> usr::shell::ExitCode { +fn format(pathname: &str) -> Result<(), ExitCode> { match parse_disk_path(pathname) { Ok((bus, dsk)) => { sys::fs::mount_ata(bus, dsk); sys::fs::format_ata(); println!("Disk successfully formatted"); println!("MFS is now mounted to '/'"); - usr::shell::ExitCode::CommandSuccessful + Ok(()) } Err(msg) => { error!("{}", msg); - usr::shell::ExitCode::CommandError + Err(ExitCode::Failure) } } } -fn erase(pathname: &str) -> usr::shell::ExitCode { +fn erase(pathname: &str) -> Result<(), ExitCode> { match parse_disk_path(pathname) { Ok((bus, dsk)) => { if let Some(drive) = Drive::open(bus, dsk) { @@ -63,7 +64,7 @@ fn erase(pathname: &str) -> usr::shell::ExitCode { if sys::console::end_of_text() || sys::console::end_of_transmission() { println!(); print!("\x1b[?25h"); // Enable cursor - return usr::shell::ExitCode::CommandError; + return Err(ExitCode::Failure); } print!("\x1b[2K\x1b[1G"); print!("Erasing block {}/{}", i, n); @@ -74,24 +75,24 @@ fn erase(pathname: &str) -> usr::shell::ExitCode { print!("\x1b[?25h"); // Enable cursor } } - usr::shell::ExitCode::CommandSuccessful + Ok(()) } Err(msg) => { error!("{}", msg); - usr::shell::ExitCode::CommandError + Err(ExitCode::Failure) } } } -fn list() -> usr::shell::ExitCode { +fn list() -> Result<(), ExitCode> { println!("Path Name (Size)"); for drive in sys::ata::list() { println!("/dev/ata/{}/{} {}", drive.bus, drive.dsk, drive); } - usr::shell::ExitCode::CommandSuccessful + Ok(()) } -fn usage() -> usr::shell::ExitCode { +fn usage() -> Result<(), ExitCode> { let size = sys::fs::disk_size(); let used = sys::fs::disk_used(); let free = size - used; @@ -102,10 +103,10 @@ fn usage() -> usr::shell::ExitCode { println!("{}size:{} {:width$} bytes", color, reset, size, width = width); println!("{}used:{} {:width$} bytes", color, reset, used, width = width); println!("{}free:{} {:width$} bytes", color, reset, free, width = width); - usr::shell::ExitCode::CommandSuccessful + Ok(()) } -fn help() -> usr::shell::ExitCode { +fn help() -> Result<(), ExitCode> { let csi_option = Style::color("LightCyan"); let csi_title = Style::color("Yellow"); let csi_reset = Style::reset(); @@ -116,5 +117,5 @@ fn help() -> usr::shell::ExitCode { println!(" {}usage{} List disk usage", csi_option, csi_reset); println!(" {}format {} Format disk", csi_option, csi_reset); println!(" {}erase {} Erase disk", csi_option, csi_reset); - usr::shell::ExitCode::CommandSuccessful + Ok(()) } diff --git a/src/usr/editor.rs b/src/usr/editor.rs index bda6caf16..040ad2d59 100644 --- a/src/usr/editor.rs +++ b/src/usr/editor.rs @@ -1,14 +1,16 @@ -use crate::{sys, usr}; +use crate::sys; use crate::api::{console, fs, io}; use crate::api::console::Style; +use crate::api::process::ExitCode; + use alloc::format; use alloc::string::{String, ToString}; use alloc::vec::Vec; use core::cmp; -pub fn main(args: &[&str]) -> usr::shell::ExitCode { +pub fn main(args: &[&str]) -> Result<(), ExitCode> { if args.len() != 2 { - return usr::shell::ExitCode::CommandError; + return Err(ExitCode::UsageError); } let pathname = args[1]; @@ -56,7 +58,7 @@ impl Editor { Self { pathname, lines, cursor, offset, config } } - pub fn save(&mut self) -> usr::shell::ExitCode { + pub fn save(&mut self) -> Result<(), ExitCode> { let mut contents = String::new(); let n = self.lines.len(); for i in 0..n { @@ -69,11 +71,11 @@ impl Editor { if fs::write(&self.pathname, contents.as_bytes()).is_ok() { let status = format!("Wrote {}L to '{}'", n, self.pathname); self.print_status(&status, "Yellow"); - usr::shell::ExitCode::CommandSuccessful + Ok(()) } else { let status = format!("Could not write to '{}'", self.pathname); self.print_status(&status, "LightRed"); - usr::shell::ExitCode::CommandError + Err(ExitCode::Failure) } } @@ -139,7 +141,7 @@ impl Editor { } } - pub fn run(&mut self) -> usr::shell::ExitCode { + pub fn run(&mut self) -> Result<(), ExitCode> { print!("\x1b[2J\x1b[1;1H"); // Clear screen and move cursor to top self.print_screen(); self.print_editing_status(); @@ -169,7 +171,7 @@ impl Editor { break; }, '\x17' => { // Ctrl W - self.save(); + self.save().ok(); print!("\x1b[?25h"); // Enable cursor continue; }, @@ -356,7 +358,7 @@ impl Editor { print!("\x1b[{};{}H", self.cursor.y + 1, self.cursor.x + 1); print!("\x1b[?25h"); // Enable cursor } - usr::shell::ExitCode::CommandSuccessful + Ok(()) } // Move cursor past end of line to end of line or left of the screen diff --git a/src/usr/elf.rs b/src/usr/elf.rs index 4a63c9b66..53c2f2316 100644 --- a/src/usr/elf.rs +++ b/src/usr/elf.rs @@ -1,11 +1,13 @@ use crate::api::console::Style; use crate::api::fs; +use crate::api::process::ExitCode; + use crate::usr; use object::{Object, ObjectSection}; -pub fn main(args: &[&str]) -> usr::shell::ExitCode { +pub fn main(args: &[&str]) -> Result<(), ExitCode> { if args.len() != 2 { - return usr::shell::ExitCode::CommandError; + return Err(ExitCode::UsageError); } let color = Style::color("Yellow"); @@ -31,13 +33,13 @@ pub fn main(args: &[&str]) -> usr::shell::ExitCode { } } } - usr::shell::ExitCode::CommandSuccessful + Ok(()) } else { println!("Could not parse ELF"); - usr::shell::ExitCode::CommandError + Err(ExitCode::Failure) } } else { println!("File not found '{}'", pathname); - usr::shell::ExitCode::CommandError + Err(ExitCode::Failure) } } diff --git a/src/usr/env.rs b/src/usr/env.rs index b6095dca7..9918bd946 100644 --- a/src/usr/env.rs +++ b/src/usr/env.rs @@ -1,30 +1,31 @@ -use crate::{sys, usr}; +use crate::api::process::ExitCode; +use crate::sys; -pub fn main(args: &[&str]) -> usr::shell::ExitCode { +pub fn main(args: &[&str]) -> Result<(), ExitCode> { match args.len() { 1 => { for (key, val) in sys::process::envs() { println!("{:10} \"{}\"", key, val); } - usr::shell::ExitCode::CommandSuccessful + Ok(()) } 2 => { let key = args[1]; if let Some(val) = sys::process::env(key) { println!("{}", val); - usr::shell::ExitCode::CommandSuccessful + Ok(()) } else { error!("Could not get '{}'", key); - usr::shell::ExitCode::CommandError + Err(ExitCode::Failure) } } 3 => { sys::process::set_env(args[1], args[2]); - usr::shell::ExitCode::CommandSuccessful + Ok(()) } _ => { error!("Invalid number of arguments"); - usr::shell::ExitCode::CommandError + Err(ExitCode::UsageError) } } } diff --git a/src/usr/find.rs b/src/usr/find.rs index fc3bba208..5dfeea932 100644 --- a/src/usr/find.rs +++ b/src/usr/find.rs @@ -1,5 +1,6 @@ -use crate::{sys, usr}; +use crate::sys; use crate::api::fs; +use crate::api::process::ExitCode; use crate::api::regex::Regex; use crate::api::console::Style; @@ -23,7 +24,7 @@ impl PrintingState { } // > find /tmp -name *.txt -line hello -pub fn main(args: &[&str]) -> usr::shell::ExitCode { +pub fn main(args: &[&str]) -> Result<(), ExitCode> { let mut path: &str = &sys::process::dir(); // TODO: use '.' let mut name = None; let mut line = None; @@ -32,7 +33,8 @@ pub fn main(args: &[&str]) -> usr::shell::ExitCode { while i < n { match args[i] { "-h" | "--help" => { - return help(); + usage(); + return Ok(()); } "-n" | "--name" => { if i + 1 < n { @@ -40,7 +42,7 @@ pub fn main(args: &[&str]) -> usr::shell::ExitCode { i += 1; } else { error!("Missing name"); - return usr::shell::ExitCode::CommandError; + return Err(ExitCode::UsageError); } }, "-l" | "--line" => { @@ -49,7 +51,7 @@ pub fn main(args: &[&str]) -> usr::shell::ExitCode { i += 1; } else { error!("Missing line"); - return usr::shell::ExitCode::CommandError; + return Err(ExitCode::UsageError); } }, _ => path = args[i], @@ -61,9 +63,14 @@ pub fn main(args: &[&str]) -> usr::shell::ExitCode { path = path.trim_end_matches('/'); } + if name.is_none() && line.is_none() { + usage(); + return Err(ExitCode::UsageError); + } + if name.is_some() { // TODO error!("`--name` is not implemented"); - return usr::shell::ExitCode::CommandError; + return Err(ExitCode::Failure); } let mut state = PrintingState::new(); @@ -71,7 +78,7 @@ pub fn main(args: &[&str]) -> usr::shell::ExitCode { print_matching_lines(path, pattern, &mut state); } - usr::shell::ExitCode::CommandSuccessful + Ok(()) } fn print_matching_lines(path: &str, pattern: &str, state: &mut PrintingState) { @@ -147,7 +154,7 @@ fn print_matching_lines_in_file(path: &str, pattern: &str, state: &mut PrintingS } } -fn help() -> usr::shell::ExitCode { +fn usage() { let csi_option = Style::color("LightCyan"); let csi_title = Style::color("Yellow"); let csi_reset = Style::reset(); @@ -156,5 +163,4 @@ fn help() -> usr::shell::ExitCode { println!("{}Options:{}", csi_title, csi_reset); println!(" {0}-n{1},{0} --name \"\"{1} Find file name matching {0}{1}", csi_option, csi_reset); println!(" {0}-l{1},{0} --line \"\"{1} Find lines matching {0}{1}", csi_option, csi_reset); - usr::shell::ExitCode::CommandSuccessful } diff --git a/src/usr/geotime.rs b/src/usr/geotime.rs index 2d5d28c6a..2d8786de6 100644 --- a/src/usr/geotime.rs +++ b/src/usr/geotime.rs @@ -1,13 +1,13 @@ -use crate::usr; use crate::api::clock; +use crate::api::process::ExitCode; use alloc::format; use core::f64::consts::PI; -pub fn main(args: &[&str]) -> usr::shell::ExitCode { +pub fn main(args: &[&str]) -> Result<(), ExitCode> { if args.len() < 2 { eprintln!("Usage: []"); - return usr::shell::ExitCode::CommandError; + return Err(ExitCode::UsageError); } let longitude = args[1].parse().expect("Could not parse longitude"); @@ -22,7 +22,7 @@ pub fn main(args: &[&str]) -> usr::shell::ExitCode { let t = libm::floor(100.0 * t) / 100.0; // Avoid rounding up 99.996 to 100.00 println!("{}", format!("{:05.2}", t).replace(".", ":")); - usr::shell::ExitCode::CommandSuccessful + Ok(()) } pub fn geotime(longitude: f64, timestamp: f64) -> f64 { diff --git a/src/usr/help.rs b/src/usr/help.rs index e673e78da..f2cc756a8 100644 --- a/src/usr/help.rs +++ b/src/usr/help.rs @@ -1,7 +1,7 @@ -use crate::usr; use crate::api::console::Style; +use crate::api::process::ExitCode; -pub fn main(args: &[&str]) -> usr::shell::ExitCode { +pub fn main(args: &[&str]) -> Result<(), ExitCode> { if args.len() > 1 { help_command(args[1]) } else { @@ -9,7 +9,7 @@ pub fn main(args: &[&str]) -> usr::shell::ExitCode { } } -fn help_command(cmd: &str) -> usr::shell::ExitCode { +fn help_command(cmd: &str) -> Result<(), ExitCode> { match cmd { "date" => help_date(), "edit" => help_edit(), @@ -17,12 +17,12 @@ fn help_command(cmd: &str) -> usr::shell::ExitCode { } } -fn help_unknown(cmd: &str) -> usr::shell::ExitCode { +fn help_unknown(cmd: &str) -> Result<(), ExitCode> { error!("Help not found for command '{}'", cmd); - usr::shell::ExitCode::CommandError + Err(ExitCode::Failure) } -fn help_summary() -> usr::shell::ExitCode { +fn help_summary() -> Result<(), ExitCode> { let csi_color = Style::color("Yellow"); let csi_reset = Style::reset(); println!("{}Commands:{}", csi_color, csi_reset); @@ -47,10 +47,10 @@ fn help_summary() -> usr::shell::ExitCode { println!(); println!("{}Credits:{}", csi_color, csi_reset); println!(" Made with <3 in 2019-2022 by Vincent Ollivier "); - usr::shell::ExitCode::CommandSuccessful + Ok(()) } -fn help_edit() -> usr::shell::ExitCode { +fn help_edit() -> Result<(), ExitCode> { let csi_color = Style::color("Yellow"); let csi_reset = Style::reset(); println!("MOROS text editor is somewhat inspired by Pico, but with an even smaller range"); @@ -71,10 +71,10 @@ fn help_edit() -> usr::shell::ExitCode { let csi_reset = Style::reset(); println!(" {}{}{} {}", csi_color, shortcut, csi_reset, usage); } - usr::shell::ExitCode::CommandSuccessful + Ok(()) } -fn help_date() -> usr::shell::ExitCode { +fn help_date() -> Result<(), ExitCode> { let csi_color = Style::color("Yellow"); let csi_reset = Style::reset(); println!("The date command's formatting behavior is based on strftime in C"); @@ -119,5 +119,5 @@ fn help_date() -> usr::shell::ExitCode { let csi_reset = Style::reset(); println!(" {}{}{} {}", csi_color, specifier, csi_reset, usage); } - usr::shell::ExitCode::CommandSuccessful + Ok(()) } diff --git a/src/usr/hex.rs b/src/usr/hex.rs index 0d6df3246..d99acb9e0 100644 --- a/src/usr/hex.rs +++ b/src/usr/hex.rs @@ -1,19 +1,19 @@ -use crate::usr; use crate::api::fs; use crate::api::console::Style; +use crate::api::process::ExitCode; // TODO: add `--skip` and `--length` params -pub fn main(args: &[&str]) -> usr::shell::ExitCode { +pub fn main(args: &[&str]) -> Result<(), ExitCode> { if args.len() != 2 { - return usr::shell::ExitCode::CommandError; + return Err(ExitCode::UsageError); } let pathname = args[1]; if let Ok(buf) = fs::read_to_bytes(pathname) { // TODO: read chunks print_hex(&buf); - usr::shell::ExitCode::CommandSuccessful + Ok(()) } else { error!("File not found '{}'", pathname); - usr::shell::ExitCode::CommandError + Err(ExitCode::Failure) } } diff --git a/src/usr/host.rs b/src/usr/host.rs index fe498ae5b..604afd428 100644 --- a/src/usr/host.rs +++ b/src/usr/host.rs @@ -1,8 +1,9 @@ use crate::{sys, usr}; use crate::api::clock; use crate::api::console::Style; -use crate::api::syscall; +use crate::api::process::ExitCode; use crate::api::random; +use crate::api::syscall; use alloc::vec; use alloc::vec::Vec; use bit_field::BitField; @@ -225,29 +226,28 @@ pub fn resolve(name: &str) -> Result { } } -pub fn main(args: &[&str]) -> usr::shell::ExitCode { +pub fn main(args: &[&str]) -> Result<(), ExitCode> { // TODO: Add `--server
` option if args.len() != 2 { help(); - return usr::shell::ExitCode::CommandError; + return Err(ExitCode::UsageError); } let domain = args[1]; match resolve(domain) { Ok(addr) => { println!("{} has address {}", domain, addr); - usr::shell::ExitCode::CommandSuccessful + Ok(()) } Err(e) => { error!("Could not resolve host: {:?}", e); - usr::shell::ExitCode::CommandError + Err(ExitCode::Failure) } } } -fn help() -> usr::shell::ExitCode { +fn help() { let csi_option = Style::color("LightCyan"); let csi_title = Style::color("Yellow"); let csi_reset = Style::reset(); println!("{}Usage:{} host {}{1}", csi_title, csi_reset, csi_option); - usr::shell::ExitCode::CommandSuccessful } diff --git a/src/usr/http.rs b/src/usr/http.rs index ba10206a4..0dc83cb5b 100644 --- a/src/usr/http.rs +++ b/src/usr/http.rs @@ -1,6 +1,7 @@ use crate::{sys, usr}; use crate::api::console::Style; use crate::api::clock; +use crate::api::process::ExitCode; use crate::api::random; use crate::api::syscall; use alloc::string::{String, ToString}; @@ -40,7 +41,7 @@ impl URL { } } -pub fn main(args: &[&str]) -> usr::shell::ExitCode { +pub fn main(args: &[&str]) -> Result<(), ExitCode> { let csi_verbose = Style::color("LightBlue"); let csi_reset = Style::reset(); @@ -59,7 +60,7 @@ pub fn main(args: &[&str]) -> usr::shell::ExitCode { } _ if args[i].starts_with("--") => { error!("Invalid option '{}'", args[i]); - return usr::shell::ExitCode::CommandError; + return Err(ExitCode::UsageError); } _ if host.is_empty() => { host = args[i] @@ -69,14 +70,14 @@ pub fn main(args: &[&str]) -> usr::shell::ExitCode { } _ => { error!("Too many arguments"); - return usr::shell::ExitCode::CommandError; + return Err(ExitCode::UsageError); } } } if host.is_empty() && path.is_empty() { error!("Missing URL"); - return usr::shell::ExitCode::CommandError; + return Err(ExitCode::UsageError); } else if path.is_empty() { if let Some(i) = args[1].find('/') { (host, path) = host.split_at(i); @@ -97,7 +98,7 @@ pub fn main(args: &[&str]) -> usr::shell::ExitCode { } Err(e) => { error!("Could not resolve host: {:?}", e); - return usr::shell::ExitCode::CommandError; + return Err(ExitCode::Failure); } } }; @@ -119,12 +120,12 @@ pub fn main(args: &[&str]) -> usr::shell::ExitCode { if clock::realtime() - started > timeout { error!("Timeout reached"); iface.remove_socket(tcp_handle); - return usr::shell::ExitCode::CommandError; + return Err(ExitCode::Failure); } if sys::console::end_of_text() || sys::console::end_of_transmission() { eprintln!(); iface.remove_socket(tcp_handle); - return usr::shell::ExitCode::CommandError; + return Err(ExitCode::Failure); } let timestamp = Instant::from_micros((clock::realtime() * 1000000.0) as i64); if let Err(e) = iface.poll(timestamp) { @@ -144,7 +145,7 @@ pub fn main(args: &[&str]) -> usr::shell::ExitCode { if socket.connect(cx, (address, url.port), local_port).is_err() { error!("Could not connect to {}:{}", address, url.port); iface.remove_socket(tcp_handle); - return usr::shell::ExitCode::CommandError; + return Err(ExitCode::Failure); } State::Request } @@ -208,13 +209,13 @@ pub fn main(args: &[&str]) -> usr::shell::ExitCode { } iface.remove_socket(tcp_handle); println!(); - usr::shell::ExitCode::CommandSuccessful + Ok(()) } else { - usr::shell::ExitCode::CommandError + Err(ExitCode::Failure) } } -fn help() -> usr::shell::ExitCode { +fn help() -> Result<(), ExitCode> { let csi_option = Style::color("LightCyan"); let csi_title = Style::color("Yellow"); let csi_reset = Style::reset(); @@ -222,5 +223,5 @@ fn help() -> usr::shell::ExitCode { println!(); println!("{}Options:{}", csi_title, csi_reset); println!(" {0}-v{1}, {0}--verbose{1} Increase verbosity", csi_option, csi_reset); - usr::shell::ExitCode::CommandSuccessful + Ok(()) } diff --git a/src/usr/httpd.rs b/src/usr/httpd.rs index a8fea2788..4985aa893 100644 --- a/src/usr/httpd.rs +++ b/src/usr/httpd.rs @@ -1,7 +1,8 @@ -use crate::{sys, usr}; +use crate::sys; use crate::api::clock; use crate::api::console::Style; use crate::api::fs; +use crate::api::process::ExitCode; use crate::api::syscall; use alloc::collections::vec_deque::VecDeque; use alloc::format; @@ -13,7 +14,7 @@ use smoltcp::time::Instant; use smoltcp::phy::Device; use time::OffsetDateTime; -pub fn main(_args: &[&str]) -> usr::shell::ExitCode { +pub fn main(_args: &[&str]) -> Result<(), ExitCode> { let csi_color = Style::color("Yellow"); let csi_reset = Style::reset(); let port = 80; @@ -32,7 +33,7 @@ pub fn main(_args: &[&str]) -> usr::shell::ExitCode { if sys::console::end_of_text() || sys::console::end_of_transmission() { iface.remove_socket(tcp_handle); println!(); - return usr::shell::ExitCode::CommandSuccessful; + return Ok(()); } let timestamp = Instant::from_micros((clock::realtime() * 1000000.0) as i64); @@ -182,7 +183,7 @@ pub fn main(_args: &[&str]) -> usr::shell::ExitCode { } } else { error!("Could not find network interface"); - usr::shell::ExitCode::CommandError + Err(ExitCode::Failure) } } diff --git a/src/usr/install.rs b/src/usr/install.rs index 2504296db..838279d2d 100644 --- a/src/usr/install.rs +++ b/src/usr/install.rs @@ -3,7 +3,10 @@ use crate::api::console::Style; use crate::api::fs; use crate::api::fs::DeviceType; use crate::api::io; +use crate::api::process::ExitCode; use crate::api::syscall; + +use alloc::format; use alloc::string::String; pub fn copy_files(verbose: bool) { @@ -58,7 +61,7 @@ pub fn copy_files(verbose: bool) { copy_file("/tmp/beep/mario.sh", include_bytes!("../../dsk/tmp/beep/mario.sh"), verbose); } -pub fn main(_args: &[&str]) -> usr::shell::ExitCode { +pub fn main(_args: &[&str]) -> Result<(), ExitCode> { let csi_color = Style::color("Yellow"); let csi_reset = Style::reset(); println!("{}Welcome to MOROS v{} installation program!{}", csi_color, env!("CARGO_PKG_VERSION"), csi_reset); @@ -70,16 +73,13 @@ pub fn main(_args: &[&str]) -> usr::shell::ExitCode { if !sys::fs::is_mounted() { println!("{}Listing disks ...{}", csi_color, csi_reset); - usr::disk::main(&["disk", "list"]); + usr::shell::exec("disk list").ok(); println!(); println!("{}Formatting disk ...{}", csi_color, csi_reset); print!("Enter path of disk to format: "); let pathname = io::stdin().read_line(); - let res = usr::disk::main(&["disk", "format", pathname.trim_end()]); - if res == usr::shell::ExitCode::CommandError { - return res; - } + usr::shell::exec(&format!("disk format {}", pathname.trim_end()))?; println!(); } @@ -91,7 +91,7 @@ pub fn main(_args: &[&str]) -> usr::shell::ExitCode { println!(); println!("{}Creating user...{}", csi_color, csi_reset); let res = usr::user::main(&["user", "create"]); - if res == usr::shell::ExitCode::CommandError { + if res == Err(ExitCode::Failure) { return res; } } @@ -102,7 +102,7 @@ pub fn main(_args: &[&str]) -> usr::shell::ExitCode { println!("Exit console or reboot to apply changes"); } - usr::shell::ExitCode::CommandSuccessful + Ok(()) } fn create_dir(pathname: &str, verbose: bool) { diff --git a/src/usr/keyboard.rs b/src/usr/keyboard.rs index e303c96ca..e5bfbf1af 100644 --- a/src/usr/keyboard.rs +++ b/src/usr/keyboard.rs @@ -1,37 +1,39 @@ -use crate::{sys, usr}; +use crate::sys; use crate::api::console::Style; +use crate::api::process::ExitCode; -pub fn main(args: &[&str]) -> usr::shell::ExitCode { +pub fn main(args: &[&str]) -> Result<(), ExitCode> { if args.len() == 1 { help(); - return usr::shell::ExitCode::CommandError; + return Err(ExitCode::UsageError); } match args[1] { "set" => { if args.len() == 2 { error!("Keyboard layout missing"); - usr::shell::ExitCode::CommandError + Err(ExitCode::Failure) } else { let layout = args[2]; if sys::keyboard::set_keyboard(layout) { - usr::shell::ExitCode::CommandSuccessful + Ok(()) } else { error!("Unknown keyboard layout"); - usr::shell::ExitCode::CommandError + Err(ExitCode::Failure) } } } "-h" | "--help" | "help" => { - help() + help(); + Ok(()) } _ => { error!("Invalid command"); - usr::shell::ExitCode::CommandError + Err(ExitCode::Failure) } } } -fn help() -> usr::shell::ExitCode { +fn help() { let csi_option = Style::color("LightCyan"); let csi_title = Style::color("Yellow"); let csi_reset = Style::reset(); @@ -39,5 +41,4 @@ fn help() -> usr::shell::ExitCode { println!(); println!("{}Commands:{}", csi_title, csi_reset); println!(" {0}set {1} Set keyboard layout", csi_option, csi_reset); - usr::shell::ExitCode::CommandSuccessful } diff --git a/src/usr/lisp.rs b/src/usr/lisp.rs index be5630b4e..576afe487 100644 --- a/src/usr/lisp.rs +++ b/src/usr/lisp.rs @@ -1,6 +1,7 @@ use crate::{api, usr}; use crate::api::fs; use crate::api::console::Style; +use crate::api::process::ExitCode; use crate::api::prompt::Prompt; use alloc::collections::BTreeMap; @@ -294,8 +295,10 @@ fn default_env() -> Rc> { data.insert("system".to_string(), Exp::Func(|args: &[Exp]| -> Result { ensure_length_eq!(args, 1); let cmd = string(&args[0])?; - let res = usr::shell::exec(&cmd); - Ok(Exp::Num(res as u8 as f64)) + match usr::shell::exec(&cmd) { + Ok(()) => Ok(Exp::Num(0.0)), + Err(code) => Ok(Exp::Num(code as u8 as f64)), + } })); data.insert("print".to_string(), Exp::Func(|args: &[Exp]| -> Result { ensure_length_eq!(args, 1); @@ -658,7 +661,7 @@ fn strip_comments(s: &str) -> String { s.split('#').next().unwrap().into() } -fn repl(env: &mut Rc>) -> usr::shell::ExitCode { +fn repl(env: &mut Rc>) -> Result<(), ExitCode> { let csi_color = Style::color("Cyan"); let csi_error = Style::color("LightRed"); let csi_reset = Style::reset(); @@ -690,10 +693,10 @@ fn repl(env: &mut Rc>) -> usr::shell::ExitCode { prompt.history.add(&line); prompt.history.save(history_file); } - usr::shell::ExitCode::CommandSuccessful + Ok(()) } -pub fn main(args: &[&str]) -> usr::shell::ExitCode { +pub fn main(args: &[&str]) -> Result<(), ExitCode> { let line_color = Style::color("Yellow"); let error_color = Style::color("LightRed"); let reset = Style::reset(); @@ -710,7 +713,7 @@ pub fn main(args: &[&str]) -> usr::shell::ExitCode { let quote = Exp::List(vec![Exp::Sym("quote".to_string()), list]); if eval_label_args(&[key, quote], env).is_err() { error!("Could not parse args"); - return usr::shell::ExitCode::CommandError; + return Err(ExitCode::Failure); } if args.len() < 2 { @@ -734,7 +737,7 @@ pub fn main(args: &[&str]) -> usr::shell::ExitCode { eprintln!("{}Error:{} {}", error_color, reset, msg); eprintln!(); eprintln!(" {}{}:{} {}", line_color, i, reset, line); - return usr::shell::ExitCode::CommandError; + return Err(ExitCode::Failure); } } } @@ -744,10 +747,10 @@ pub fn main(args: &[&str]) -> usr::shell::ExitCode { } } } - usr::shell::ExitCode::CommandSuccessful + Ok(()) } else { error!("File not found '{}'", pathname); - usr::shell::ExitCode::CommandError + Err(ExitCode::Failure) } } } diff --git a/src/usr/list.rs b/src/usr/list.rs index 5151b44d3..11821d034 100644 --- a/src/usr/list.rs +++ b/src/usr/list.rs @@ -1,14 +1,15 @@ -use crate::{sys, usr}; +use crate::sys; use crate::api::console::Style; use crate::api::time; use crate::api::fs; +use crate::api::process::ExitCode; use crate::api::syscall; use crate::api::fs::FileInfo; use alloc::string::ToString; use alloc::vec::Vec; -pub fn main(args: &[&str]) -> usr::shell::ExitCode { +pub fn main(args: &[&str]) -> Result<(), ExitCode> { let mut path: &str = &sys::process::dir(); // TODO: use '.' let mut sort = "name"; let mut hide_dot_files = true; @@ -44,7 +45,7 @@ pub fn main(args: &[&str]) -> usr::shell::ExitCode { "time" => files.sort_by_key(|f| f.time()), _ => { error!("Invalid sort key '{}'", sort); - return usr::shell::ExitCode::CommandError; + return Err(ExitCode::Failure); } } @@ -58,18 +59,18 @@ pub fn main(args: &[&str]) -> usr::shell::ExitCode { for file in files { print_file(file, width); } - usr::shell::ExitCode::CommandSuccessful + Ok(()) } else { error!("Could not read directory '{}'", path); - usr::shell::ExitCode::CommandError + Err(ExitCode::Failure) } } else { print_file(&info, info.size().to_string().len()); - usr::shell::ExitCode::CommandSuccessful + Ok(()) } } else { error!("Could not find file or directory '{}'", path); - usr::shell::ExitCode::CommandError + Err(ExitCode::Failure) } } @@ -89,7 +90,7 @@ fn print_file(file: &FileInfo, width: usize) { println!("{:width$} {} {}{}{}", file.size(), date.format("%F %H:%M:%S"), color, file.name(), csi_reset, width = width); } -fn help() -> usr::shell::ExitCode { +fn help() -> Result<(), ExitCode> { let csi_option = Style::color("LightCyan"); let csi_title = Style::color("Yellow"); let csi_reset = Style::reset(); @@ -100,5 +101,5 @@ fn help() -> usr::shell::ExitCode { println!(" {0}-n{1},{0} --name{1} Sort by name", csi_option, csi_reset); println!(" {0}-s{1},{0} --size{1} Sort by size", csi_option, csi_reset); println!(" {0}-t{1},{0} --time{1} Sort by time", csi_option, csi_reset); - usr::shell::ExitCode::CommandSuccessful + Ok(()) } diff --git a/src/usr/memory.rs b/src/usr/memory.rs index 5ca612a15..aabd3b7bd 100644 --- a/src/usr/memory.rs +++ b/src/usr/memory.rs @@ -1,23 +1,26 @@ -use crate::{sys, usr}; +use crate::sys; use crate::api::console::Style; +use crate::api::process::ExitCode; + use alloc::string::ToString; -pub fn main(args: &[&str]) -> usr::shell::ExitCode { +pub fn main(args: &[&str]) -> Result<(), ExitCode> { if args.len() == 1 || args[1] == "usage" { - usage() + usage(); + Ok(()) } else if args[1] == "format" { sys::fs::mount_mem(); sys::fs::format_mem(); println!("Memory successfully formatted"); println!("MFS is now mounted to '/'"); - usr::shell::ExitCode::CommandSuccessful + Ok(()) } else { help(); - usr::shell::ExitCode::CommandError + Err(ExitCode::Failure) } } -fn usage() -> usr::shell::ExitCode { +fn usage() { let size = sys::allocator::memory_size(); let used = sys::allocator::memory_used(); let free = size - used; @@ -28,10 +31,9 @@ fn usage() -> usr::shell::ExitCode { println!("{}size:{} {:width$} bytes", color, reset, size, width = width); println!("{}used:{} {:width$} bytes", color, reset, used, width = width); println!("{}free:{} {:width$} bytes", color, reset, free, width = width); - usr::shell::ExitCode::CommandSuccessful } -fn help() -> usr::shell::ExitCode { +fn help() { let csi_option = Style::color("LightCyan"); let csi_title = Style::color("Yellow"); let csi_reset = Style::reset(); @@ -40,5 +42,4 @@ fn help() -> usr::shell::ExitCode { println!("{}Commands:{}", csi_title, csi_reset); println!(" {}usage{} List memory usage", csi_option, csi_reset); println!(" {}format{} Format RAM disk", csi_option, csi_reset); - usr::shell::ExitCode::CommandSuccessful } diff --git a/src/usr/move.rs b/src/usr/move.rs index a424052d5..8dc4e6660 100644 --- a/src/usr/move.rs +++ b/src/usr/move.rs @@ -1,13 +1,14 @@ +use crate::api::process::ExitCode; use crate::usr; -pub fn main(args: &[&str]) -> usr::shell::ExitCode { +pub fn main(args: &[&str]) -> Result<(), ExitCode> { if args.len() != 3 { - return usr::shell::ExitCode::CommandError; + return Err(ExitCode::UsageError); } // TODO: Avoid doing copy+delete match usr::copy::main(args) { - usr::shell::ExitCode::CommandSuccessful => usr::delete::main(&args[0..2]), - _ => usr::shell::ExitCode::CommandError, + Ok(()) => usr::delete::main(&args[0..2]), + _ => Err(ExitCode::Failure), } } diff --git a/src/usr/net.rs b/src/usr/net.rs index 987d55d05..e17ff65ba 100644 --- a/src/usr/net.rs +++ b/src/usr/net.rs @@ -1,9 +1,10 @@ -use crate::{sys, usr, debug}; +use crate::sys; use alloc::format; use crate::api::clock; use crate::api::console::Style; use crate::api::fs; use crate::api::syscall; +use crate::api::process::ExitCode; use crate::sys::net::EthernetDeviceIO; use alloc::borrow::ToOwned; @@ -16,15 +17,16 @@ use smoltcp::socket::{TcpSocket, TcpSocketBuffer}; use smoltcp::time::Instant; use smoltcp::phy::Device; -pub fn main(args: &[&str]) -> usr::shell::ExitCode { +pub fn main(args: &[&str]) -> Result<(), ExitCode> { if args.len() == 1 { help(); - return usr::shell::ExitCode::CommandError; + return Err(ExitCode::UsageError); } match args[1] { "-h" | "--help" => { - return help(); + help(); + return Ok(()); } "config" => { if args.len() < 3 { @@ -33,7 +35,8 @@ pub fn main(args: &[&str]) -> usr::shell::ExitCode { print_config("gw"); print_config("dns"); } else if args[2] == "-h" || args[2] == "--help" { - return help_config(); + help_config(); + return Ok(()); } else if args.len() < 4 { print_config(args[2]); } else { @@ -65,7 +68,7 @@ pub fn main(args: &[&str]) -> usr::shell::ExitCode { if sys::console::end_of_text() || sys::console::end_of_transmission() { println!(); iface.remove_socket(tcp_handle); - return usr::shell::ExitCode::CommandSuccessful; + return Ok(()); } syscall::sleep(0.1); @@ -90,13 +93,13 @@ pub fn main(args: &[&str]) -> usr::shell::ExitCode { } _ => { error!("Invalid command"); - return usr::shell::ExitCode::CommandError; + return Err(ExitCode::Failure); } } - usr::shell::ExitCode::CommandSuccessful + Ok(()) } -fn help() -> usr::shell::ExitCode { +fn help() { let csi_option = Style::color("LightCyan"); let csi_title = Style::color("Yellow"); let csi_reset = Style::reset(); @@ -106,10 +109,9 @@ fn help() -> usr::shell::ExitCode { println!(" {}config{} Configure network", csi_option, csi_reset); println!(" {}monitor{} Monitor network", csi_option, csi_reset); println!(" {}stat{} Display network status", csi_option, csi_reset); - usr::shell::ExitCode::CommandSuccessful } -fn help_config() -> usr::shell::ExitCode { +fn help_config() { let csi_option = Style::color("LightCyan"); let csi_title = Style::color("Yellow"); let csi_reset = Style::reset(); @@ -120,7 +122,6 @@ fn help_config() -> usr::shell::ExitCode { println!(" {}ip{} IP Address", csi_option, csi_reset); println!(" {}gw{} Gateway Address", csi_option, csi_reset); println!(" {}dns{} Domain Name Servers", csi_option, csi_reset); - usr::shell::ExitCode::CommandSuccessful } fn print_config(attribute: &str) { diff --git a/src/usr/pci.rs b/src/usr/pci.rs index a4a6f2ea4..c5033cca5 100644 --- a/src/usr/pci.rs +++ b/src/usr/pci.rs @@ -1,7 +1,8 @@ -use crate::{sys, usr}; use crate::api::console::Style; +use crate::api::process::ExitCode; +use crate::sys; -pub fn main(args: &[&str]) -> usr::shell::ExitCode { +pub fn main(args: &[&str]) -> Result<(), ExitCode> { if args.len() == 1 { return list(false); } @@ -17,7 +18,7 @@ pub fn main(args: &[&str]) -> usr::shell::ExitCode { } } -fn list(verbose: bool) -> usr::shell::ExitCode { +fn list(verbose: bool) -> Result<(), ExitCode> { let color1 = Style::color("Blue"); let color2 = Style::color("LightBlue"); let reset = Style::reset(); @@ -37,10 +38,10 @@ fn list(verbose: bool) -> usr::shell::ExitCode { println!(); } } - usr::shell::ExitCode::CommandSuccessful + Ok(()) } -fn help() -> usr::shell::ExitCode { +fn help() -> Result<(), ExitCode> { let csi_option = Style::color("LightCyan"); let csi_title = Style::color("Yellow"); let csi_reset = Style::reset(); @@ -51,5 +52,5 @@ fn help() -> usr::shell::ExitCode { println!(); println!("{}Options:{}", csi_title, csi_reset); println!(" {0}-v{1}, {0}--verbose{1} Increase verbosity", csi_option, csi_reset); - usr::shell::ExitCode::CommandSuccessful + Ok(()) } diff --git a/src/usr/pow.rs b/src/usr/pow.rs index 1af6fc3df..b04b5e8d7 100644 --- a/src/usr/pow.rs +++ b/src/usr/pow.rs @@ -1,6 +1,7 @@ -use crate::usr; use crate::api::console::Style; +use crate::api::process::ExitCode; use crate::api::{io, random, console}; + use core::fmt; use alloc::format; use alloc::string::ToString; @@ -12,11 +13,11 @@ struct Game { board: [usize; 16], } -pub fn main(_args: &[&str]) -> usr::shell::ExitCode { +pub fn main(_args: &[&str]) -> Result<(), ExitCode> { print!("\x1b[?25l"); // Disable cursor Game::new().run(); print!("\x1b[?25h"); // Enable cursor - usr::shell::ExitCode::CommandSuccessful + Ok(()) } impl Game { diff --git a/src/usr/read.rs b/src/usr/read.rs index 7db1cb3cb..5c3d6e5aa 100644 --- a/src/usr/read.rs +++ b/src/usr/read.rs @@ -2,15 +2,16 @@ use crate::{api, sys, usr}; use crate::api::console; use crate::api::fs; use crate::api::syscall; +use crate::api::process::ExitCode; use crate::sys::cmos::CMOS; use alloc::borrow::ToOwned; use alloc::vec::Vec; use core::convert::TryInto; -pub fn main(args: &[&str]) -> usr::shell::ExitCode { +pub fn main(args: &[&str]) -> Result<(), ExitCode> { if args.len() != 2 { - return usr::shell::ExitCode::CommandError; + return Err(ExitCode::UsageError); } let mut path = args[1]; @@ -30,7 +31,7 @@ pub fn main(args: &[&str]) -> usr::shell::ExitCode { rtc.year, rtc.month, rtc.day, rtc.hour, rtc.minute, rtc.second ); - usr::shell::ExitCode::CommandSuccessful + Ok(()) }, _ => { if path.starts_with("/net/") { @@ -42,7 +43,7 @@ pub fn main(args: &[&str]) -> usr::shell::ExitCode { let parts: Vec<_> = path.split('/').collect(); if parts.len() < 4 { eprintln!("Usage: read /net/http//"); - usr::shell::ExitCode::CommandError + Err(ExitCode::Failure) } else { match parts[2] { "tcp" => { @@ -61,7 +62,7 @@ pub fn main(args: &[&str]) -> usr::shell::ExitCode { } _ => { error!("Unknown protocol '{}'", parts[2]); - usr::shell::ExitCode::CommandError + Err(ExitCode::Failure) } } } @@ -69,10 +70,10 @@ pub fn main(args: &[&str]) -> usr::shell::ExitCode { if info.is_file() { if let Ok(contents) = api::fs::read_to_string(path) { print!("{}", contents); - usr::shell::ExitCode::CommandSuccessful + Ok(()) } else { error!("Could not read '{}'", path); - usr::shell::ExitCode::CommandError + Err(ExitCode::Failure) } } else if info.is_dir() { usr::list::main(args) @@ -82,18 +83,18 @@ pub fn main(args: &[&str]) -> usr::shell::ExitCode { loop { if sys::console::end_of_text() || sys::console::end_of_transmission() { println!(); - return usr::shell::ExitCode::CommandSuccessful; + return Ok(()); } if let Ok(bytes) = fs::read_to_bytes(path) { if is_char_device && bytes.len() == 1 { match bytes[0] as char { console::ETX_KEY => { println!("^C"); - return usr::shell::ExitCode::CommandSuccessful; + return Ok(()); } console::EOT_KEY => { println!("^D"); - return usr::shell::ExitCode::CommandSuccessful; + return Ok(()); } _ => {} } @@ -101,7 +102,7 @@ pub fn main(args: &[&str]) -> usr::shell::ExitCode { if is_float_device { if bytes.len() == 8 { println!("{:.6}", f64::from_be_bytes(bytes[0..8].try_into().unwrap())); - return usr::shell::ExitCode::CommandSuccessful; + return Ok(()); } } for b in bytes { @@ -109,16 +110,16 @@ pub fn main(args: &[&str]) -> usr::shell::ExitCode { } } else { error!("Could not read '{}'", path); - return usr::shell::ExitCode::CommandError; + return Err(ExitCode::Failure); } } } else { error!("Could not read type of '{}'", path); - usr::shell::ExitCode::CommandError + Err(ExitCode::Failure) } } else { error!("File not found '{}'", path); - usr::shell::ExitCode::CommandError + Err(ExitCode::Failure) } } } diff --git a/src/usr/shell.rs b/src/usr/shell.rs index f0a4e5272..555a1be0b 100644 --- a/src/usr/shell.rs +++ b/src/usr/shell.rs @@ -1,6 +1,7 @@ use crate::{api, sys, usr}; use crate::api::console::Style; use crate::api::fs; +use crate::api::process::ExitCode; use crate::api::prompt::Prompt; use crate::api::regex::Regex; use crate::api::syscall; @@ -19,15 +20,6 @@ const AUTOCOMPLETE_COMMANDS: [&str; 35] = [ "shell", "socket", "tcp", "time", "user", "vga", "write" ]; -#[repr(u8)] -#[derive(PartialEq, Eq)] -pub enum ExitCode { - CommandSuccessful = 0, - CommandUnknown = 1, - CommandError = 2, - ShellExit = 255, -} - struct Config { env: BTreeMap, aliases: BTreeMap, @@ -41,6 +33,7 @@ impl Config { env.insert(key, val); // Copy the process environment to the shell environment } env.insert("DIR".to_string(), sys::process::dir()); + env.insert("?".to_string(), "0".to_string()); Config { env, aliases } } } @@ -196,16 +189,16 @@ pub fn split_args(cmd: &str) -> Vec { args } -fn cmd_proc(args: &[&str]) -> ExitCode { +fn cmd_proc(args: &[&str]) -> Result<(), ExitCode> { match args.len() { 1 => { - ExitCode::CommandSuccessful + Ok(()) }, 2 => { match args[1] { "id" => { println!("{}", sys::process::id()); - ExitCode::CommandSuccessful + Ok(()) } "files" => { for (i, handle) in sys::process::file_handles().iter().enumerate() { @@ -213,24 +206,24 @@ fn cmd_proc(args: &[&str]) -> ExitCode { println!("{}: {:?}", i, resource); } } - ExitCode::CommandSuccessful + Ok(()) } _ => { - ExitCode::CommandError + Err(ExitCode::Failure) } } }, _ => { - ExitCode::CommandError + Err(ExitCode::Failure) } } } -fn cmd_change_dir(args: &[&str], config: &mut Config) -> ExitCode { +fn cmd_change_dir(args: &[&str], config: &mut Config) -> Result<(), ExitCode> { match args.len() { 1 => { println!("{}", sys::process::dir()); - ExitCode::CommandSuccessful + Ok(()) }, 2 => { let mut pathname = fs::realpath(args[1]); @@ -240,82 +233,85 @@ fn cmd_change_dir(args: &[&str], config: &mut Config) -> ExitCode { if api::fs::is_dir(&pathname) { sys::process::set_dir(&pathname); config.env.insert("DIR".to_string(), sys::process::dir()); - ExitCode::CommandSuccessful + Ok(()) } else { error!("File not found '{}'", pathname); - ExitCode::CommandError + Err(ExitCode::Failure) } }, _ => { - ExitCode::CommandError + Err(ExitCode::Failure) } } } -fn cmd_alias(args: &[&str], config: &mut Config) -> ExitCode { +fn cmd_alias(args: &[&str], config: &mut Config) -> Result<(), ExitCode> { if args.len() != 3 { let csi_option = Style::color("LightCyan"); let csi_title = Style::color("Yellow"); let csi_reset = Style::reset(); println!("{}Usage:{} alias {} {1}", csi_title, csi_reset, csi_option); - return usr::shell::ExitCode::CommandError; + return Err(ExitCode::Failure); } config.aliases.insert(args[1].to_string(), args[2].to_string()); - ExitCode::CommandSuccessful + Ok(()) } -fn cmd_unalias(args: &[&str], config: &mut Config) -> ExitCode { +fn cmd_unalias(args: &[&str], config: &mut Config) -> Result<(), ExitCode> { if args.len() != 2 { let csi_option = Style::color("LightCyan"); let csi_title = Style::color("Yellow"); let csi_reset = Style::reset(); println!("{}Usage:{} unalias {}{1}", csi_title, csi_reset, csi_option); - return usr::shell::ExitCode::CommandError; + return Err(ExitCode::Failure); } if config.aliases.remove(&args[1].to_string()).is_none() { error!("Error: could not unalias '{}'", args[1]); - return usr::shell::ExitCode::CommandError; + return Err(ExitCode::Failure); } - ExitCode::CommandSuccessful + Ok(()) } -fn cmd_set(args: &[&str], config: &mut Config) -> ExitCode { +fn cmd_set(args: &[&str], config: &mut Config) -> Result<(), ExitCode> { if args.len() != 3 { let csi_option = Style::color("LightCyan"); let csi_title = Style::color("Yellow"); let csi_reset = Style::reset(); println!("{}Usage:{} set {} {1}", csi_title, csi_reset, csi_option); - return usr::shell::ExitCode::CommandError; + return Err(ExitCode::Failure); } config.env.insert(args[1].to_string(), args[2].to_string()); - ExitCode::CommandSuccessful + Ok(()) } -fn cmd_unset(args: &[&str], config: &mut Config) -> ExitCode { +fn cmd_unset(args: &[&str], config: &mut Config) -> Result<(), ExitCode> { if args.len() != 2 { let csi_option = Style::color("LightCyan"); let csi_title = Style::color("Yellow"); let csi_reset = Style::reset(); println!("{}Usage:{} unset {}{1}", csi_title, csi_reset, csi_option); - return usr::shell::ExitCode::CommandError; + return Err(ExitCode::Failure); } if config.env.remove(&args[1].to_string()).is_none() { error!("Error: could not unset '{}'", args[1]); - return usr::shell::ExitCode::CommandError; + return Err(ExitCode::Failure); } - ExitCode::CommandSuccessful + Ok(()) } -fn exec_with_config(cmd: &str, config: &mut Config) -> ExitCode { +fn exec_with_config(cmd: &str, config: &mut Config) -> Result<(), ExitCode> { + #[cfg(test)] // FIXME: tests with `print foo => /bar` are failing without that + sys::console::print_fmt(format_args!("")); + let mut cmd = cmd.to_string(); // Replace `$key` with its value in the environment or an empty string - let re = Regex::new("\\$\\w+"); + let re = Regex::new("\\$\\S+"); while let Some((a, b)) = re.find(&cmd) { let key: String = cmd.chars().skip(a + 1).take(b - a - 1).collect(); let val = config.env.get(&key).map_or("", String::as_str); @@ -372,24 +368,24 @@ fn exec_with_config(cmd: &str, config: &mut Config) -> ExitCode { is_redirected = true; if i == n - 1 { println!("Could not parse path for redirection"); - return ExitCode::CommandError; + return Err(ExitCode::Failure); } let path = args[i + 1]; if api::fs::reopen(path, left_handle).is_err() { println!("Could not open path for redirection"); - return ExitCode::CommandError; + return Err(ExitCode::Failure); } args.remove(i); // Remove redirection from args args.remove(i); // Remove path from args n -= 2; } else if is_thin_arrow { // TODO: Implement pipes println!("Could not parse arrow"); - return ExitCode::CommandError; + return Err(ExitCode::Failure); } } let res = match args[0] { - "" => ExitCode::CommandSuccessful, + "" => Ok(()), "2048" => usr::pow::main(&args), "alias" => cmd_alias(&args, config), "base64" => usr::base64::main(&args), @@ -422,7 +418,7 @@ fn exec_with_config(cmd: &str, config: &mut Config) -> ExitCode { "net" => usr::net::main(&args), "pci" => usr::pci::main(&args), "proc" => cmd_proc(&args), - "quit" => ExitCode::ShellExit, + "quit" => Err(ExitCode::ShellExit), "read" => usr::read::main(&args), "set" => cmd_set(&args, config), "shell" => usr::shell::main(&args), @@ -443,29 +439,20 @@ fn exec_with_config(cmd: &str, config: &mut Config) -> ExitCode { Some(FileType::Dir) => { sys::process::set_dir(&path); config.env.insert("DIR".to_string(), sys::process::dir()); - ExitCode::CommandSuccessful + Ok(()) } Some(FileType::File) => { - if api::process::spawn(&path, &args[1..]).is_ok() { - // TODO: get exit code - ExitCode::CommandSuccessful - } else { - error!("'{}' is not executable", path); - ExitCode::CommandError - } + spawn(&path, &args) } _ => { - if api::process::spawn(&format!("/bin/{}", args[0]), &args).is_ok() { - ExitCode::CommandSuccessful - } else { - error!("Could not execute '{}'", cmd); - ExitCode::CommandUnknown - } + let path = format!("/bin/{}", args[0]); + spawn(&path, &args) } } } }; + // TODO: Remove this when redirections are done in spawned process if is_redirected { for i in 0..3 { @@ -476,7 +463,25 @@ fn exec_with_config(cmd: &str, config: &mut Config) -> ExitCode { res } -fn repl(config: &mut Config) -> usr::shell::ExitCode { +fn spawn(path: &str, args: &[&str]) -> Result<(), ExitCode> { + match api::process::spawn(&path, &args) { + Err(ExitCode::ExecError) => { + error!("Could not execute '{}'", args[0]); + Err(ExitCode::ExecError) + } + Err(ExitCode::ReadError) => { + error!("Could not read '{}'", args[0]); + Err(ExitCode::ReadError) + } + Err(ExitCode::OpenError) => { + error!("Could not open '{}'", args[0]); + Err(ExitCode::OpenError) + } + res => res, + } +} + +fn repl(config: &mut Config) -> Result<(), ExitCode> { println!(); let mut prompt = Prompt::new(); @@ -484,39 +489,34 @@ fn repl(config: &mut Config) -> usr::shell::ExitCode { prompt.history.load(history_file); prompt.completion.set(&shell_completer); - let mut success = true; - while let Some(cmd) = prompt.input(&prompt_string(success)) { - match exec_with_config(&cmd, config) { - ExitCode::CommandSuccessful => { - success = true; - }, - ExitCode::ShellExit => { - break; - }, - _ => { - success = false; - }, - } + let mut code = ExitCode::Success; + while let Some(cmd) = prompt.input(&prompt_string(code == ExitCode::Success)) { + code = match exec_with_config(&cmd, config) { + Err(ExitCode::ShellExit) => break, + Err(e) => e, + Ok(()) => ExitCode::Success, + }; + config.env.insert("?".to_string(), format!("{}", code as u8)); prompt.history.add(&cmd); prompt.history.save(history_file); sys::console::drain(); println!(); } print!("\x1b[2J\x1b[1;1H"); // Clear screen and move cursor to top - ExitCode::CommandSuccessful + Ok(()) } -pub fn exec(cmd: &str) -> ExitCode { +pub fn exec(cmd: &str) -> Result<(), ExitCode> { let mut config = Config::new(); exec_with_config(cmd, &mut config) } -pub fn main(args: &[&str]) -> ExitCode { +pub fn main(args: &[&str]) -> Result<(), ExitCode> { let mut config = Config::new(); if let Ok(rc) = fs::read_to_string("/ini/shell.sh") { for cmd in rc.split('\n') { - exec_with_config(cmd, &mut config); + exec_with_config(cmd, &mut config).ok(); } } @@ -536,13 +536,13 @@ pub fn main(args: &[&str]) -> ExitCode { if let Ok(contents) = api::fs::read_to_string(pathname) { for line in contents.split('\n') { if !line.is_empty() { - exec_with_config(line, &mut config); + exec_with_config(line, &mut config).ok(); } } - ExitCode::CommandSuccessful + Ok(()) } else { println!("File not found '{}'", pathname); - ExitCode::CommandError + Err(ExitCode::Failure) } } } @@ -556,24 +556,24 @@ fn test_shell() { usr::install::copy_files(false); // Redirect standard output - exec("print test1 => /test"); + exec("print test1 => /test").ok(); assert_eq!(api::fs::read_to_string("/test"), Ok("test1\n".to_string())); // Overwrite content of existing file - exec("print test2 => /test"); + exec("print test2 => /test").ok(); assert_eq!(api::fs::read_to_string("/test"), Ok("test2\n".to_string())); // Redirect standard output explicitely - exec("print test3 1=> /test"); + exec("print test3 1=> /test").ok(); assert_eq!(api::fs::read_to_string("/test"), Ok("test3\n".to_string())); // Redirect standard error explicitely - exec("hex /nope 2=> /test"); + exec("hex /nope 2=> /test").ok(); assert!(api::fs::read_to_string("/test").unwrap().contains("File not found '/nope'")); let mut config = Config::new(); - exec_with_config("set b 42", &mut config); - exec_with_config("print a $b $c d => /test", &mut config); + exec_with_config("set b 42", &mut config).ok(); + exec_with_config("print a $b $c d => /test", &mut config).ok(); assert_eq!(api::fs::read_to_string("/test"), Ok("a 42 d\n".to_string())); sys::fs::dismount(); diff --git a/src/usr/socket.rs b/src/usr/socket.rs index 98b26f44e..36f5e074b 100644 --- a/src/usr/socket.rs +++ b/src/usr/socket.rs @@ -2,8 +2,9 @@ use crate::{sys, usr, debug}; use crate::api::console::Style; use crate::api::clock; use crate::api::io; -use crate::api::syscall; +use crate::api::process::ExitCode; use crate::api::random; +use crate::api::syscall; use alloc::format; use alloc::string::String; @@ -14,7 +15,7 @@ use smoltcp::socket::{TcpSocket, TcpSocketBuffer, TcpState}; use smoltcp::time::Instant; use smoltcp::wire::IpAddress; -pub fn main(args: &[&str]) -> usr::shell::ExitCode { +pub fn main(args: &[&str]) -> Result<(), ExitCode> { let mut listen = false; let mut prompt = false; let mut verbose = false; @@ -72,7 +73,7 @@ pub fn main(args: &[&str]) -> usr::shell::ExitCode { if args.len() != required_args_count { help(); - return usr::shell::ExitCode::CommandError; + return Err(ExitCode::UsageError); } let host = if listen { "0.0.0.0" } else { &args[1] }; @@ -87,7 +88,7 @@ pub fn main(args: &[&str]) -> usr::shell::ExitCode { } Err(e) => { error!("Could not resolve host: {:?}", e); - return usr::shell::ExitCode::CommandError; + return Err(ExitCode::Failure); } } }; @@ -107,7 +108,7 @@ pub fn main(args: &[&str]) -> usr::shell::ExitCode { if sys::console::end_of_text() || sys::console::end_of_transmission() { eprintln!(); iface.remove_socket(tcp_handle); - return usr::shell::ExitCode::CommandError; + return Err(ExitCode::Failure); } let timestamp = Instant::from_micros((clock::realtime() * 1000000.0) as i64); if let Err(e) = iface.poll(timestamp) { @@ -143,7 +144,7 @@ pub fn main(args: &[&str]) -> usr::shell::ExitCode { } if socket.connect(cx, (address, port), local_port).is_err() { error!("Could not connect to {}:{}", address, port); - return usr::shell::ExitCode::CommandError; + return Err(ExitCode::Failure); } } State::Receiving @@ -216,13 +217,13 @@ pub fn main(args: &[&str]) -> usr::shell::ExitCode { } } iface.remove_socket(tcp_handle); - usr::shell::ExitCode::CommandSuccessful + Ok(()) } else { - usr::shell::ExitCode::CommandError + Err(ExitCode::Failure) } } -fn help() -> usr::shell::ExitCode { +fn help() { let csi_option = Style::color("LightCyan"); let csi_title = Style::color("Yellow"); let csi_reset = Style::reset(); @@ -234,5 +235,4 @@ fn help() -> usr::shell::ExitCode { println!(" {0}-p{1}, {0}--prompt{1} Display prompt", csi_option, csi_reset); println!(" {0}-r{1}, {0}--read-only{1} Read only connexion", csi_option, csi_reset); println!(" {0}-i{1}, {0}--interval