@@ -40,6 +40,15 @@ impl ToShortFileName for &str {
4040 }
4141}
4242
43+ /// How filenames should be handled
44+ #[ derive( Debug , Copy , Clone , PartialEq , Eq ) ]
45+ enum FilenameKind {
46+ /// Stored mixed case
47+ VolumeLabel ,
48+ /// Converted to upper case
49+ DirectoryEntry ,
50+ }
51+
4352/// An MS-DOS 8.3 filename. 7-bit ASCII only. All lower-case is converted to
4453/// upper-case by default.
4554#[ cfg_attr( feature = "defmt-log" , derive( defmt:: Format ) ) ]
@@ -82,6 +91,26 @@ impl ShortFileName {
8291
8392 /// Create a new MS-DOS 8.3 space-padded file name as stored in the directory entry.
8493 pub fn create_from_str ( name : & str ) -> Result < ShortFileName , FilenameError > {
94+ Self :: create_from_str_inner ( name, FilenameKind :: DirectoryEntry )
95+ }
96+
97+ /// Create a new Volume Label.
98+ pub fn create_volume_label_from_str ( name : & str ) -> Result < ShortFileName , FilenameError > {
99+ Self :: create_from_str_inner ( name, FilenameKind :: VolumeLabel )
100+ }
101+
102+ /// Create a new MS-DOS 8.3 space-padded file name as stored in the
103+ /// directory entry.
104+ ///
105+ /// The Unicode string must contain only code points from ISO-8859-1 (i.e.
106+ /// Code Points \U+0000 to \U+00FF).
107+ ///
108+ /// Convert the filename to upper case if specified by the `case_handling`
109+ /// argument.
110+ fn create_from_str_inner (
111+ name : & str ,
112+ case_handling : FilenameKind ,
113+ ) -> Result < ShortFileName , FilenameError > {
85114 let mut sfn = ShortFileName {
86115 contents : [ b' ' ; Self :: FILENAME_MAX_LEN ] ,
87116 } ;
@@ -98,92 +127,42 @@ impl ShortFileName {
98127
99128 let mut idx = 0 ;
100129 let mut seen_dot = false ;
101- for ch in name. bytes ( ) {
130+ for ch in name. chars ( ) {
102131 match ch {
103132 // Microsoft say these are the invalid characters
104- 0x00 ..=0x1F
105- | 0x20
106- | 0x22
107- | 0x2A
108- | 0x2B
109- | 0x2C
110- | 0x2F
111- | 0x3A
112- | 0x3B
113- | 0x3C
114- | 0x3D
115- | 0x3E
116- | 0x3F
117- | 0x5B
118- | 0x5C
119- | 0x5D
120- | 0x7C => {
133+ '\u{0000}' ..='\u{001F}'
134+ | '"'
135+ | '*'
136+ | '+'
137+ | ','
138+ | '/'
139+ | ':'
140+ | ';'
141+ | '<'
142+ | '='
143+ | '>'
144+ | '?'
145+ | '['
146+ | '\\'
147+ | ']'
148+ | '|' => {
121149 return Err ( FilenameError :: InvalidCharacter ) ;
122150 }
123- // Denotes the start of the file extension
124- b'.' => {
125- if ( 1 ..=Self :: FILENAME_BASE_MAX_LEN ) . contains ( & idx) {
126- idx = Self :: FILENAME_BASE_MAX_LEN ;
127- seen_dot = true ;
128- } else {
129- return Err ( FilenameError :: MisplacedPeriod ) ;
130- }
131- }
132- _ => {
133- let ch = ch. to_ascii_uppercase ( ) ;
134- if seen_dot {
135- if ( Self :: FILENAME_BASE_MAX_LEN ..Self :: FILENAME_MAX_LEN ) . contains ( & idx) {
136- sfn. contents [ idx] = ch;
137- } else {
138- return Err ( FilenameError :: NameTooLong ) ;
139- }
140- } else if idx < Self :: FILENAME_BASE_MAX_LEN {
141- sfn. contents [ idx] = ch;
142- } else {
143- return Err ( FilenameError :: NameTooLong ) ;
144- }
145- idx += 1 ;
151+ ' ' if case_handling == FilenameKind :: DirectoryEntry => {
152+ // can't have spaces in regular file names
153+ return Err ( FilenameError :: InvalidCharacter ) ;
146154 }
147- }
148- }
149- if idx == 0 {
150- return Err ( FilenameError :: FilenameEmpty ) ;
151- }
152- Ok ( sfn)
153- }
154-
155- /// Create a new MS-DOS 8.3 space-padded file name as stored in the directory entry.
156- /// Use this for volume labels with mixed case.
157- pub fn create_from_str_mixed_case ( name : & str ) -> Result < ShortFileName , FilenameError > {
158- let mut sfn = ShortFileName {
159- contents : [ b' ' ; Self :: FILENAME_MAX_LEN ] ,
160- } ;
161- let mut idx = 0 ;
162- let mut seen_dot = false ;
163- for ch in name. bytes ( ) {
164- match ch {
165- // Microsoft say these are the invalid characters
166- 0x00 ..=0x1F
167- | 0x20
168- | 0x22
169- | 0x2A
170- | 0x2B
171- | 0x2C
172- | 0x2F
173- | 0x3A
174- | 0x3B
175- | 0x3C
176- | 0x3D
177- | 0x3E
178- | 0x3F
179- | 0x5B
180- | 0x5C
181- | 0x5D
182- | 0x7C => {
155+ x if x > '\u{00FF}' => {
156+ // We only handle ISO-8859-1 which is Unicode Code Points
157+ // \U+0000 to \U+00FF. This is above that.
183158 return Err ( FilenameError :: InvalidCharacter ) ;
184159 }
185- // Denotes the start of the file extension
186- b'.' => {
160+ '.' if case_handling == FilenameKind :: VolumeLabel => {
161+ // not allowed
162+ return Err ( FilenameError :: MisplacedPeriod ) ;
163+ }
164+ '.' => {
165+ // Denotes the start of the file extension
187166 if ( 1 ..=Self :: FILENAME_BASE_MAX_LEN ) . contains ( & idx) {
188167 idx = Self :: FILENAME_BASE_MAX_LEN ;
189168 seen_dot = true ;
@@ -192,14 +171,23 @@ impl ShortFileName {
192171 }
193172 }
194173 _ => {
174+ let b = if case_handling == FilenameKind :: DirectoryEntry {
175+ ch. to_ascii_uppercase ( ) as u8
176+ } else {
177+ ch as u8
178+ } ;
195179 if seen_dot {
196180 if ( Self :: FILENAME_BASE_MAX_LEN ..Self :: FILENAME_MAX_LEN ) . contains ( & idx) {
197- sfn. contents [ idx] = ch ;
181+ sfn. contents [ idx] = b ;
198182 } else {
199183 return Err ( FilenameError :: NameTooLong ) ;
200184 }
201185 } else if idx < Self :: FILENAME_BASE_MAX_LEN {
202- sfn. contents [ idx] = ch;
186+ sfn. contents [ idx] = b;
187+ } else if idx < Self :: FILENAME_MAX_LEN
188+ && case_handling == FilenameKind :: VolumeLabel
189+ {
190+ sfn. contents [ idx] = b;
203191 } else {
204192 return Err ( FilenameError :: NameTooLong ) ;
205193 }
@@ -337,6 +325,17 @@ mod test {
337325 assert ! ( ShortFileName :: create_from_str( "123456789" ) . is_err( ) ) ;
338326 assert ! ( ShortFileName :: create_from_str( "12345678.ABCD" ) . is_err( ) ) ;
339327 }
328+
329+ #[ test]
330+ fn volume_name ( ) {
331+ let sfn = ShortFileName {
332+ contents : * b"Hello \xA3 99 " ,
333+ } ;
334+ assert_eq ! (
335+ sfn,
336+ ShortFileName :: create_volume_label_from_str( "Hello £99" ) . unwrap( )
337+ )
338+ }
340339}
341340
342341// ****************************************************************************
0 commit comments