@@ -61,12 +61,11 @@ impl Context {
61
61
}
62
62
63
63
/// 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| {
70
69
println ! (
71
70
"{:12} {:9} {} {} {:X?} {:?}" ,
72
71
entry. name, entry. size, entry. ctime, entry. mtime, entry. cluster, entry. attributes
@@ -84,7 +83,9 @@ impl Context {
84
83
return Ok ( ( ) ) ;
85
84
} ;
86
85
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" ) ;
88
89
s. directory = d;
89
90
if filename == ".." {
90
91
s. path . pop ( ) ;
@@ -96,14 +97,9 @@ impl Context {
96
97
97
98
/// print a text file
98
99
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 ) ?;
107
103
let mut data = Vec :: new ( ) ;
108
104
while !f. is_eof ( ) {
109
105
let mut buffer = vec ! [ 0u8 ; 65536 ] ;
@@ -122,14 +118,9 @@ impl Context {
122
118
123
119
/// print a binary file
124
120
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 ) ?;
133
124
let mut data = Vec :: new ( ) ;
134
125
while !f. is_eof ( ) {
135
126
let mut buffer = vec ! [ 0u8 ; 65536 ] ;
@@ -164,12 +155,9 @@ impl Context {
164
155
165
156
/// create a directory
166
157
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)
173
161
}
174
162
175
163
fn process_line ( & mut self , line : & str ) -> Result < ( ) , Error > {
@@ -184,7 +172,9 @@ impl Context {
184
172
} else if line == "3:" {
185
173
self . current_volume = 3 ;
186
174
} 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 ( ) ) ?;
188
178
} else if line == "stat" {
189
179
self . stat ( ) ?;
190
180
} else if let Some ( dirname) = line. strip_prefix ( "cd " ) {
@@ -200,6 +190,91 @@ impl Context {
200
190
}
201
191
Ok ( ( ) )
202
192
}
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
+ }
203
278
}
204
279
205
280
impl Drop for Context {
0 commit comments