Skip to content

Commit 2b07077

Browse files
thejpsterjonathanpallant
authored andcommitted
Shell supports relative paths.
Handle relative paths in "mkdir", "dir", "cat" and "hexdump". Not currently supported in "cd".
1 parent fdd59dc commit 2b07077

File tree

1 file changed

+105
-30
lines changed

1 file changed

+105
-30
lines changed

examples/shell.rs

Lines changed: 105 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -61,12 +61,11 @@ impl Context {
6161
}
6262

6363
/// Print a directory listing
64-
fn dir(&mut self) -> Result<(), Error> {
65-
let Some(s) = &self.volumes[self.current_volume] else {
66-
println!("That volume isn't available");
67-
return Ok(());
68-
};
69-
self.volume_mgr.iterate_dir(s.directory, |entry| {
64+
fn dir(&mut self, path: &str) -> Result<(), Error> {
65+
println!("Directory listing of {:?}", path);
66+
let dir = self.resolve_existing_directory(path)?;
67+
let mut dir = dir.to_directory(&mut self.volume_mgr);
68+
dir.iterate_dir(|entry| {
7069
println!(
7170
"{:12} {:9} {} {} {:X?} {:?}",
7271
entry.name, entry.size, entry.ctime, entry.mtime, entry.cluster, entry.attributes
@@ -84,7 +83,9 @@ impl Context {
8483
return Ok(());
8584
};
8685
let d = self.volume_mgr.open_dir(s.directory, filename)?;
87-
self.volume_mgr.close_dir(s.directory)?;
86+
self.volume_mgr
87+
.close_dir(s.directory)
88+
.expect("close open dir");
8889
s.directory = d;
8990
if filename == ".." {
9091
s.path.pop();
@@ -96,14 +97,9 @@ impl Context {
9697

9798
/// print a text file
9899
fn cat(&mut self, filename: &str) -> Result<(), Error> {
99-
let Some(s) = &mut self.volumes[self.current_volume] else {
100-
println!("This volume isn't available");
101-
return Ok(());
102-
};
103-
let mut f = self
104-
.volume_mgr
105-
.open_file_in_dir(s.directory, filename, embedded_sdmmc::Mode::ReadOnly)?
106-
.to_file(&mut self.volume_mgr);
100+
let (dir, filename) = self.resolve_filename(filename)?;
101+
let mut dir = dir.to_directory(&mut self.volume_mgr);
102+
let mut f = dir.open_file_in_dir(filename, embedded_sdmmc::Mode::ReadOnly)?;
107103
let mut data = Vec::new();
108104
while !f.is_eof() {
109105
let mut buffer = vec![0u8; 65536];
@@ -122,14 +118,9 @@ impl Context {
122118

123119
/// print a binary file
124120
fn hexdump(&mut self, filename: &str) -> Result<(), Error> {
125-
let Some(s) = &mut self.volumes[self.current_volume] else {
126-
println!("This volume isn't available");
127-
return Ok(());
128-
};
129-
let mut f = self
130-
.volume_mgr
131-
.open_file_in_dir(s.directory, filename, embedded_sdmmc::Mode::ReadOnly)?
132-
.to_file(&mut self.volume_mgr);
121+
let (dir, filename) = self.resolve_filename(filename)?;
122+
let mut dir = dir.to_directory(&mut self.volume_mgr);
123+
let mut f = dir.open_file_in_dir(filename, embedded_sdmmc::Mode::ReadOnly)?;
133124
let mut data = Vec::new();
134125
while !f.is_eof() {
135126
let mut buffer = vec![0u8; 65536];
@@ -164,12 +155,9 @@ impl Context {
164155

165156
/// create a directory
166157
fn mkdir(&mut self, dir_name: &str) -> Result<(), Error> {
167-
let Some(s) = &mut self.volumes[self.current_volume] else {
168-
println!("This volume isn't available");
169-
return Ok(());
170-
};
171-
// make the dir
172-
self.volume_mgr.make_dir_in_dir(s.directory, dir_name)
158+
let (dir, filename) = self.resolve_filename(dir_name)?;
159+
let mut dir = dir.to_directory(&mut self.volume_mgr);
160+
dir.make_dir_in_dir(filename)
173161
}
174162

175163
fn process_line(&mut self, line: &str) -> Result<(), Error> {
@@ -184,7 +172,9 @@ impl Context {
184172
} else if line == "3:" {
185173
self.current_volume = 3;
186174
} else if line == "dir" {
187-
self.dir()?;
175+
self.dir(".")?;
176+
} else if let Some(dirname) = line.strip_prefix("dir ") {
177+
self.dir(dirname.trim())?;
188178
} else if line == "stat" {
189179
self.stat()?;
190180
} else if let Some(dirname) = line.strip_prefix("cd ") {
@@ -200,6 +190,91 @@ impl Context {
200190
}
201191
Ok(())
202192
}
193+
194+
/// Resolves an existing directory.
195+
///
196+
/// Converts a string path into a directory handle.
197+
///
198+
/// * Bare names (no leading `.`, `/` or `N:/`) are mapped to the current
199+
/// directory in the current volume.
200+
/// * Relative names, like `../SOMEDIR` or `./SOMEDIR`, traverse
201+
/// starting at the current volume and directory.
202+
/// * Absolute, like `1:/SOMEDIR/OTHERDIR` start at the given volume.
203+
fn resolve_existing_directory(&mut self, full_path: &str) -> Result<RawDirectory, Error> {
204+
let (dir, fragment) = self.resolve_filename(full_path)?;
205+
let mut work_dir = dir.to_directory(&mut self.volume_mgr);
206+
work_dir.change_dir(fragment)?;
207+
Ok(work_dir.to_raw_directory())
208+
}
209+
210+
/// Resolves a filename.
211+
///
212+
/// Converts a string path into a directory handle and a name within that
213+
/// directory (that may or may not exist).
214+
///
215+
/// * Bare names (no leading `.`, `/` or `N:/`) are mapped to the current
216+
/// directory in the current volume.
217+
/// * Relative names, like `../SOMEDIR/SOMEFILE` or `./SOMEDIR/SOMEFILE`, traverse
218+
/// starting at the current volume and directory.
219+
/// * Absolute, like `1:/SOMEDIR/SOMEFILE` start at the given volume.
220+
fn resolve_filename<'path>(
221+
&mut self,
222+
full_path: &'path str,
223+
) -> Result<(RawDirectory, &'path str), Error> {
224+
let mut volume_idx = self.current_volume;
225+
let mut path_fragments = if full_path.is_empty() { "." } else { full_path };
226+
let mut is_absolute = false;
227+
if let Some((given_volume_idx, remainder)) =
228+
Self::is_absolute(full_path, VolumeIdx(self.current_volume))
229+
{
230+
volume_idx = given_volume_idx.0;
231+
path_fragments = remainder;
232+
is_absolute = true;
233+
}
234+
let Some(s) = &mut self.volumes[volume_idx] else {
235+
return Err(Error::NoSuchVolume);
236+
};
237+
let mut work_dir = if is_absolute {
238+
// relative to root
239+
self.volume_mgr
240+
.open_root_dir(s.volume)?
241+
.to_directory(&mut self.volume_mgr)
242+
} else {
243+
// relative to CWD
244+
self.volume_mgr
245+
.open_dir(s.directory, ".")?
246+
.to_directory(&mut self.volume_mgr)
247+
};
248+
249+
let mut path_iter = path_fragments.split('/').peekable();
250+
let mut last_piece = ".";
251+
while let Some(fragment) = path_iter.next() {
252+
if path_iter.peek().is_none() {
253+
// this is the last piece
254+
last_piece = fragment;
255+
break;
256+
}
257+
work_dir.change_dir(fragment)?;
258+
}
259+
260+
Ok((work_dir.to_raw_directory(), last_piece))
261+
}
262+
263+
/// Is this an absolute path?
264+
fn is_absolute(path: &str, current_volume: VolumeIdx) -> Option<(VolumeIdx, &str)> {
265+
if let Some(remainder) = path.strip_prefix("0:/") {
266+
Some((VolumeIdx(0), remainder))
267+
} else if let Some(remainder) = path.strip_prefix("1:/") {
268+
Some((VolumeIdx(1), remainder))
269+
} else if let Some(remainder) = path.strip_prefix("2:/") {
270+
Some((VolumeIdx(2), remainder))
271+
} else if let Some(remainder) = path.strip_prefix("3:/") {
272+
Some((VolumeIdx(3), remainder))
273+
} else {
274+
path.strip_prefix('/')
275+
.map(|remainder| (current_volume, remainder))
276+
}
277+
}
203278
}
204279

205280
impl Drop for Context {

0 commit comments

Comments
 (0)