Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add a little shell example. #100

Merged
merged 5 commits into from
Oct 9, 2023
Merged
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
Added hexdump and cat.
Also moved the code into a method so we can use ?. Although ? is a pain when your objects cannot safely be dropped.
  • Loading branch information
thejpster committed Oct 3, 2023
commit a041eee5648e425c9fb52fc161a72cd2495288b6
261 changes: 185 additions & 76 deletions examples/shell.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,158 @@ use crate::linux::{Clock, LinuxBlockDevice};

mod linux;

struct State {
struct VolumeState {
directory: Directory,
volume: Volume,
path: Vec<String>,
}

struct Context {
volume_mgr: VolumeManager<LinuxBlockDevice, Clock, 8, 8, 4>,
volumes: [Option<VolumeState>; 4],
current_volume: usize,
}

impl Context {
fn current_path(&self) -> Vec<String> {
let Some(s) = &self.volumes[self.current_volume] else {
return vec![];
};
s.path.clone()
}

fn process_line(&mut self, line: &str) -> Result<(), Error<std::io::Error>> {
if line == "help" {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

FYI, https://docs.rs/logos/latest/logos/ is really nice for things like this :)

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Interesting! I'll check it out.

println!("Commands:");
println!("\thelp -> this help text");
println!("\t<volume>: -> change volume/partition");
println!("\tdir -> do a directory listing");
println!("\tstat -> print volume manager status");
println!("\tcat <file> -> print a text file");
println!("\thexdump <file> -> print a binary file");
println!("\tcd .. -> go up a level");
println!("\tcd <dir> -> change into <dir>");
println!("\tquit -> exits the program");
} else if line == "0:" {
self.current_volume = 0;
} else if line == "1:" {
self.current_volume = 1;
} else if line == "2:" {
self.current_volume = 2;
} else if line == "3:" {
self.current_volume = 3;
} else if line == "stat" {
println!("Status:\n{:#?}", self.volume_mgr);
} else if line == "dir" {
let Some(s) = &self.volumes[self.current_volume] else {
println!("That volume isn't available");
return Ok(());
};
self.volume_mgr.iterate_dir(s.directory, |entry| {
println!(
"{:12} {:9} {} {}",
entry.name,
entry.size,
entry.mtime,
if entry.attributes.is_directory() {
"<DIR>"
} else {
""
}
);
})?;
} else if let Some(arg) = line.strip_prefix("cd ") {
let Some(s) = &mut self.volumes[self.current_volume] else {
println!("This volume isn't available");
return Ok(());
};
let d = self.volume_mgr.open_dir(s.directory, arg)?;
self.volume_mgr.close_dir(s.directory)?;
s.directory = d;
if arg == ".." {
s.path.pop();
} else {
s.path.push(arg.to_owned());
}
} else if let Some(arg) = line.strip_prefix("cat ") {
let Some(s) = &mut self.volumes[self.current_volume] else {
println!("This volume isn't available");
return Ok(());
};
let f = self.volume_mgr.open_file_in_dir(
s.directory,
arg,
embedded_sdmmc::Mode::ReadOnly,
)?;
let mut inner = || -> Result<(), Error<std::io::Error>> {
let mut data = Vec::new();
while !self.volume_mgr.file_eof(f)? {
let mut buffer = vec![0u8; 65536];
let n = self.volume_mgr.read(f, &mut buffer)?;
// read n bytes
data.extend_from_slice(&buffer[0..n]);
println!("Read {} bytes, making {} total", n, data.len());
}
if let Ok(s) = std::str::from_utf8(&data) {
println!("{}", s);
} else {
println!("I'm afraid that file isn't UTF-8 encoded");
}
Ok(())
};
let r = inner();
self.volume_mgr.close_file(f)?;
r?;
} else if let Some(arg) = line.strip_prefix("hexdump ") {
let Some(s) = &mut self.volumes[self.current_volume] else {
println!("This volume isn't available");
return Ok(());
};
let f = self.volume_mgr.open_file_in_dir(
s.directory,
arg,
embedded_sdmmc::Mode::ReadOnly,
)?;
let mut inner = || -> Result<(), Error<std::io::Error>> {
let mut data = Vec::new();
while !self.volume_mgr.file_eof(f)? {
let mut buffer = vec![0u8; 65536];
let n = self.volume_mgr.read(f, &mut buffer)?;
// read n bytes
data.extend_from_slice(&buffer[0..n]);
println!("Read {} bytes, making {} total", n, data.len());
}
for (idx, chunk) in data.chunks(16).enumerate() {
print!("{:08x} | ", idx * 16);
for b in chunk {
print!("{:02x} ", b);
}
for _padding in 0..(16 - chunk.len()) {
print!(" ");
}
print!("| ");
for b in chunk {
print!(
"{}",
if b.is_ascii_graphic() {
*b as char
} else {
'.'
}
);
}
println!();
}
Ok(())
};
let r = inner();
self.volume_mgr.close_file(f)?;
r?;
} else {
println!("Unknown command {line:?} - try 'help' for help");
}
Ok(())
}
}

fn main() -> Result<(), Error<std::io::Error>> {
Expand All @@ -22,29 +171,33 @@ fn main() -> Result<(), Error<std::io::Error>> {
let print_blocks = args.find(|x| x == "-v").map(|_| true).unwrap_or(false);
println!("Opening '{filename}'...");
let lbd = LinuxBlockDevice::new(filename, print_blocks).map_err(Error::DeviceError)?;
let mut volume_mgr: VolumeManager<LinuxBlockDevice, Clock, 8, 8, 4> =
VolumeManager::new_with_limits(lbd, Clock, 100);
let stdin = std::io::stdin();
let mut volumes: [Option<State>; 4] = [None, None, None, None];

let mut ctx = Context {
volume_mgr: VolumeManager::new_with_limits(lbd, Clock, 100),
volumes: [None, None, None, None],
current_volume: 0,
};

let mut current_volume = None;
for volume_no in 0..4 {
match volume_mgr.open_volume(VolumeIdx(volume_no)) {
match ctx.volume_mgr.open_volume(VolumeIdx(volume_no)) {
Ok(volume) => {
println!("Volume # {}: found", volume_no,);
match volume_mgr.open_root_dir(volume) {
match ctx.volume_mgr.open_root_dir(volume) {
Ok(root_dir) => {
volumes[volume_no] = Some(State {
ctx.volumes[volume_no] = Some(VolumeState {
directory: root_dir,
volume,
path: vec![],
});
if current_volume.is_none() {
current_volume = Some(volume_no);
}
}
Err(e) => {
println!("Failed to open root directory: {e:?}");
volume_mgr.close_volume(volume).expect("close volume");
ctx.volume_mgr.close_volume(volume).expect("close volume");
}
}
}
Expand All @@ -54,95 +207,51 @@ fn main() -> Result<(), Error<std::io::Error>> {
}
}

let Some(mut current_volume) = current_volume else {
println!("No volumes found in file. Sorry.");
return Ok(());
match current_volume {
Some(n) => {
// Default to the first valid partition
ctx.current_volume = n;
}
None => {
println!("No volumes found in file. Sorry.");
return Ok(());
}
};

loop {
print!("{}:> ", current_volume);
print!("{}:/", ctx.current_volume);
print!("{}", ctx.current_path().join("/"));
print!("> ");
std::io::stdout().flush().unwrap();
let mut line = String::new();
stdin.read_line(&mut line)?;
let line = line.trim();
if line == "quit" {
break;
} else if line == "help" {
println!("Commands:");
println!("\thelp -> this help text");
println!("\t<volume>: -> change volume/partition");
println!("\tdir -> do a directory listing");
println!("\tquit -> exits the program");
} else if line == "0:" {
current_volume = 0;
} else if line == "1:" {
current_volume = 1;
} else if line == "2:" {
current_volume = 2;
} else if line == "3:" {
current_volume = 3;
} else if line == "stat" {
println!("Status:\n{volume_mgr:#?}");
} else if line == "dir" {
let Some(s) = &volumes[current_volume] else {
println!("That volume isn't available");
continue;
};
let r = volume_mgr.iterate_dir(s.directory, |entry| {
println!(
"{:12} {:9} {} {}",
entry.name,
entry.size,
entry.mtime,
if entry.attributes.is_directory() {
"<DIR>"
} else {
""
}
);
});
handle("iterating directory", r);
} else if let Some(arg) = line.strip_prefix("cd ") {
let Some(s) = &mut volumes[current_volume] else {
println!("This volume isn't available");
continue;
};
match volume_mgr.open_dir(s.directory, arg) {
Ok(d) => {
let r = volume_mgr.close_dir(s.directory);
handle("closing old directory", r);
s.directory = d;
}
Err(e) => {
handle("changing directory", Err(e));
}
}
} else {
println!("Unknown command {line:?} - try 'help' for help");
} else if let Err(e) = ctx.process_line(line) {
println!("Error: {:?}", e);
}
}

for (idx, s) in volumes.into_iter().enumerate() {
for (idx, s) in ctx.volumes.into_iter().enumerate() {
if let Some(state) = s {
println!("Closing current dir for {idx}...");
let r = ctx.volume_mgr.close_dir(state.directory);
if let Err(e) = r {
println!("Error closing directory: {e:?}");
}
println!("Unmounting {idx}...");
let r = volume_mgr.close_dir(state.directory);
handle("closing directory", r);
let r = volume_mgr.close_volume(state.volume);
handle("closing volume", r);
println!("Unmounted {idx}!");
let r = ctx.volume_mgr.close_volume(state.volume);
if let Err(e) = r {
println!("Error closing volume: {e:?}");
}
}
}

println!("Bye!");
Ok(())
}

fn handle(operation: &str, r: Result<(), Error<std::io::Error>>) {
if let Err(e) = r {
println!("Error {operation}: {e:?}");
}
}

// ****************************************************************************
//
// End Of File
Expand Down