@@ -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
205280impl Drop for Context {
0 commit comments