1 | /** |
---|
2 | * @file |
---|
3 | * |
---|
4 | * @brief MDOS Date Conversion |
---|
5 | * @ingroup libfs_msdos MSDOS FileSystem |
---|
6 | */ |
---|
7 | |
---|
8 | /* |
---|
9 | * Written by Paul Popelka (paulp@uts.amdahl.com) |
---|
10 | * |
---|
11 | * You can do anything you want with this software, just don't say you wrote |
---|
12 | * it, and don't remove this notice. |
---|
13 | * |
---|
14 | * This software is provided "as is". |
---|
15 | * |
---|
16 | * The author supplies this software to be publicly redistributed on the |
---|
17 | * understanding that the author is not responsible for the correct |
---|
18 | * functioning of this software in any circumstances and is not liable for |
---|
19 | * any damages caused by this software. |
---|
20 | * |
---|
21 | * Adaptation of NetBSD code for RTEMS by Victor V. Vengerov <vvv@oktet.ru> |
---|
22 | * $NetBSD: msdosfs_conv.c,v 1.10 1994/12/27 18:36:24 mycroft Exp $ |
---|
23 | * |
---|
24 | * October 1992 |
---|
25 | * |
---|
26 | * Modifications to support UTF-8 in the file system are |
---|
27 | * Copyright (c) 2013 embedded brains GmbH. |
---|
28 | */ |
---|
29 | |
---|
30 | #if HAVE_CONFIG_H |
---|
31 | #include "config.h" |
---|
32 | #endif |
---|
33 | |
---|
34 | #include <ctype.h> |
---|
35 | #include <rtems.h> |
---|
36 | #include "msdos.h" |
---|
37 | |
---|
38 | /* #define SECONDSPERDAY (24 * 60 * 60) */ |
---|
39 | #define SECONDSPERDAY ((uint32_t) 86400) |
---|
40 | |
---|
41 | #define UTF8_MAX_CHAR_SIZE 4 |
---|
42 | #define UTF8_NULL 0x00 |
---|
43 | #define UTF8_NULL_SIZE 1 |
---|
44 | #define UTF8_BLANK 0x20 |
---|
45 | #define UTF8_BLANK_SIZE 1 |
---|
46 | #define UTF8_FULL_STOP 0x2e |
---|
47 | #define UTF8_FULL_STOP_SIZE 1 |
---|
48 | |
---|
49 | #define UTF16_MAX_CHAR_SIZE 4 |
---|
50 | #define UTF16_NULL CT_LE_W( 0x0000 ) |
---|
51 | #define UTF16_NULL_SIZE 2 |
---|
52 | #define UTF16_BLANK CT_LE_W( 0x0020 ) |
---|
53 | #define UTF16_BLANK_SIZE 2 |
---|
54 | #define UTF16_FULL_STOP CT_LE_W( 0x002e ) |
---|
55 | #define UTF16_FULL_STOP_SIZE 2 |
---|
56 | |
---|
57 | /* |
---|
58 | * Days in each month in a regular year. |
---|
59 | */ |
---|
60 | static uint16_t regyear[] = { |
---|
61 | 31, 28, 31, 30, 31, 30, |
---|
62 | 31, 31, 30, 31, 30, 31 |
---|
63 | }; |
---|
64 | |
---|
65 | /* |
---|
66 | * Days in each month in a leap year. |
---|
67 | */ |
---|
68 | static uint16_t leapyear[] = { |
---|
69 | 31, 29, 31, 30, 31, 30, |
---|
70 | 31, 31, 30, 31, 30, 31 |
---|
71 | }; |
---|
72 | |
---|
73 | /* |
---|
74 | * Variables used to remember parts of the last time conversion. Maybe we |
---|
75 | * can avoid a full conversion. |
---|
76 | */ |
---|
77 | static uint32_t lasttime; |
---|
78 | static uint32_t lastday; |
---|
79 | static uint16_t lastddate; |
---|
80 | static uint16_t lastdtime; |
---|
81 | |
---|
82 | /* |
---|
83 | * Convert the unix version of time to dos's idea of time to be used in |
---|
84 | * file timestamps. The passed in unix time is assumed to be in GMT. |
---|
85 | */ |
---|
86 | void |
---|
87 | msdos_date_unix2dos(unsigned int t, uint16_t *ddp, |
---|
88 | uint16_t *dtp) |
---|
89 | { |
---|
90 | uint32_t days; |
---|
91 | uint32_t inc; |
---|
92 | uint32_t year; |
---|
93 | uint32_t month; |
---|
94 | uint16_t *months; |
---|
95 | |
---|
96 | /* |
---|
97 | * If the time from the last conversion is the same as now, then |
---|
98 | * skip the computations and use the saved result. |
---|
99 | */ |
---|
100 | if (lasttime != t) { |
---|
101 | lasttime = t; |
---|
102 | lastdtime = (((t % 60) >> 1) << MSDOS_DT_2SECONDS_SHIFT) |
---|
103 | + (((t / 60) % 60) << MSDOS_DT_MINUTES_SHIFT) |
---|
104 | + (((t / 3600) % 24) << MSDOS_DT_HOURS_SHIFT); |
---|
105 | |
---|
106 | /* |
---|
107 | * If the number of days since 1970 is the same as the last |
---|
108 | * time we did the computation then skip all this leap year |
---|
109 | * and month stuff. |
---|
110 | */ |
---|
111 | days = t / (SECONDSPERDAY); |
---|
112 | if (days != lastday) { |
---|
113 | lastday = days; |
---|
114 | for (year = 1970;; year++) { |
---|
115 | inc = year & 0x03 ? 365 : 366; |
---|
116 | if (days < inc) |
---|
117 | break; |
---|
118 | days -= inc; |
---|
119 | } |
---|
120 | months = year & 0x03 ? regyear : leapyear; |
---|
121 | for (month = 0; month < 12; month++) { |
---|
122 | if (days < months[month]) |
---|
123 | break; |
---|
124 | days -= months[month]; |
---|
125 | } |
---|
126 | lastddate = ((days + 1) << MSDOS_DD_DAY_SHIFT) |
---|
127 | + ((month + 1) << MSDOS_DD_MONTH_SHIFT); |
---|
128 | /* |
---|
129 | * Remember dos's idea of time is relative to 1980. |
---|
130 | * unix's is relative to 1970. If somehow we get a |
---|
131 | * time before 1980 then don't give totally crazy |
---|
132 | * results. |
---|
133 | */ |
---|
134 | if (year > 1980) |
---|
135 | lastddate += (year - 1980) << |
---|
136 | MSDOS_DD_YEAR_SHIFT; |
---|
137 | } |
---|
138 | } |
---|
139 | *dtp = lastdtime; |
---|
140 | *ddp = lastddate; |
---|
141 | } |
---|
142 | |
---|
143 | /* |
---|
144 | * The number of days between Jan 1, 1970 and Jan 1, 1980. In that |
---|
145 | * interval there were 8 regular years and 2 leap years. |
---|
146 | */ |
---|
147 | /* #define DAYSTO1980 ((8 * 365) + (2 * 366)) */ |
---|
148 | #define DAYSTO1980 ((uint32_t) 3652) |
---|
149 | |
---|
150 | static uint16_t lastdosdate; |
---|
151 | static uint32_t lastseconds; |
---|
152 | |
---|
153 | /* |
---|
154 | * Convert from dos' idea of time to unix'. This will probably only be |
---|
155 | * called from the stat(), and fstat() system calls and so probably need |
---|
156 | * not be too efficient. |
---|
157 | */ |
---|
158 | unsigned int |
---|
159 | msdos_date_dos2unix(unsigned int dd, unsigned int dt) |
---|
160 | { |
---|
161 | uint32_t seconds; |
---|
162 | uint32_t m, month; |
---|
163 | uint32_t y, year; |
---|
164 | uint32_t days; |
---|
165 | uint16_t *months; |
---|
166 | |
---|
167 | seconds = 2 * ((dt & MSDOS_DT_2SECONDS_MASK) >> MSDOS_DT_2SECONDS_SHIFT) |
---|
168 | + ((dt & MSDOS_DT_MINUTES_MASK) >> MSDOS_DT_MINUTES_SHIFT) * 60 |
---|
169 | + ((dt & MSDOS_DT_HOURS_MASK) >> MSDOS_DT_HOURS_SHIFT) * 3600; |
---|
170 | /* |
---|
171 | * If the year, month, and day from the last conversion are the |
---|
172 | * same then use the saved value. |
---|
173 | */ |
---|
174 | if (lastdosdate != dd) { |
---|
175 | lastdosdate = dd; |
---|
176 | days = 0; |
---|
177 | year = (dd & MSDOS_DD_YEAR_MASK) >> MSDOS_DD_YEAR_SHIFT; |
---|
178 | for (y = 0; y < year; y++) |
---|
179 | days += y & 0x03 ? 365 : 366; |
---|
180 | months = year & 0x03 ? regyear : leapyear; |
---|
181 | /* |
---|
182 | * Prevent going from 0 to 0xffffffff in the following |
---|
183 | * loop. |
---|
184 | */ |
---|
185 | month = (dd & MSDOS_DD_MONTH_MASK) >> MSDOS_DD_MONTH_SHIFT; |
---|
186 | if (month == 0) { |
---|
187 | month = 1; |
---|
188 | } |
---|
189 | for (m = 0; m < month - 1; m++) |
---|
190 | days += months[m]; |
---|
191 | days += ((dd & MSDOS_DD_DAY_MASK) >> MSDOS_DD_DAY_SHIFT) - 1; |
---|
192 | lastseconds = (days + DAYSTO1980) * SECONDSPERDAY; |
---|
193 | } |
---|
194 | return seconds + lastseconds; |
---|
195 | } |
---|
196 | |
---|
197 | |
---|
198 | static const uint8_t codepage_valid_char_map[] = { |
---|
199 | 0, 0, 0, 0, 0, 0, 0, 0, /* 00-07 */ |
---|
200 | 0, 0, 0, 0, 0, 0, 0, 0, /* 08-0f */ |
---|
201 | 0, 0, 0, 0, 0, 0, 0, 0, /* 10-17 */ |
---|
202 | 0, 0, 0, 0, 0, 0, 0, 0, /* 18-1f */ |
---|
203 | 0x20, 0x21, 0, 0x23, 0x24, 0x25, 0x26, 0x27, /* 20-27 */ |
---|
204 | 0x28, 0x29, 0, 0, 0, 0x2d, 0, 0, /* 28-2f */ |
---|
205 | 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, /* 30-37 */ |
---|
206 | 0x38, 0x39, 0, 0, 0, 0, 0, 0, /* 38-3f */ |
---|
207 | 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, /* 40-47 */ |
---|
208 | 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, /* 48-4f */ |
---|
209 | 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, /* 50-57 */ |
---|
210 | 0x58, 0x59, 0x5a, 0, 0, 0, 0x5e, 0x5f, /* 58-5f */ |
---|
211 | 0x60, 0, 0, 0, 0, 0, 0, 0, /* 60-67 */ |
---|
212 | 0, 0, 0, 0, 0, 0, 0, 0, /* 68-6f */ |
---|
213 | 0, 0, 0, 0, 0, 0, 0, 0, /* 70-77 */ |
---|
214 | 0, 0, 0, 0x7b, 0, 0x7d, 0x7e, 0, /* 78-7f */ |
---|
215 | 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, /* 80-87 */ |
---|
216 | 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f, /* 88-8f */ |
---|
217 | 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, /* 90-97 */ |
---|
218 | 0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f, /* 98-9f */ |
---|
219 | 0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, /* a0-a7 */ |
---|
220 | 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf, /* a8-af */ |
---|
221 | 0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, /* b0-b7 */ |
---|
222 | 0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf, /* b8-bf */ |
---|
223 | 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, /* c0-c7 */ |
---|
224 | 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf, /* c8-cf */ |
---|
225 | 0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, /* d0-d7 */ |
---|
226 | 0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf, /* d8-df */ |
---|
227 | 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, /* e0-e7 */ |
---|
228 | 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef, /* e8-ef */ |
---|
229 | 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, /* f0-f7 */ |
---|
230 | 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff /* f8-ff */ |
---|
231 | }; |
---|
232 | |
---|
233 | static uint16_t |
---|
234 | msdos_get_valid_utf16_filename_character (const uint16_t utf16_character) |
---|
235 | { |
---|
236 | uint16_t retval = 0x0000; |
---|
237 | uint16_t char_num = CF_LE_W( utf16_character ); |
---|
238 | |
---|
239 | if ( char_num <= 0x00ff ) { |
---|
240 | switch ( char_num ) |
---|
241 | { |
---|
242 | case 0x002b: /* '+' */ |
---|
243 | case 0x002c: /* ',' */ |
---|
244 | case 0x002e: /* '.' */ |
---|
245 | case 0x003b: /* ';' */ |
---|
246 | case 0x003d: /* '=' */ |
---|
247 | case 0x005b: /* '[' */ |
---|
248 | case 0x005d: /* ']' */ |
---|
249 | case 0x0061: /* 'a' */ |
---|
250 | case 0x0062: /* 'b' */ |
---|
251 | case 0x0063: /* 'c' */ |
---|
252 | case 0x0064: /* 'd' */ |
---|
253 | case 0x0065: /* 'e' */ |
---|
254 | case 0x0066: /* 'f' */ |
---|
255 | case 0x0067: /* 'g' */ |
---|
256 | case 0x0068: /* 'h' */ |
---|
257 | case 0x0069: /* 'i' */ |
---|
258 | case 0x006a: /* 'j' */ |
---|
259 | case 0x006b: /* 'k' */ |
---|
260 | case 0x006c: /* 'l' */ |
---|
261 | case 0x006d: /* 'm' */ |
---|
262 | case 0x006e: /* 'n' */ |
---|
263 | case 0x006f: /* 'o' */ |
---|
264 | case 0x0070: /* 'p' */ |
---|
265 | case 0x0071: /* 'q' */ |
---|
266 | case 0x0072: /* 'r' */ |
---|
267 | case 0x0073: /* 's' */ |
---|
268 | case 0x0074: /* 't' */ |
---|
269 | case 0x0075: /* 'u' */ |
---|
270 | case 0x0076: /* 'v' */ |
---|
271 | case 0x0077: /* 'w' */ |
---|
272 | case 0x0078: /* 'x' */ |
---|
273 | case 0x0079: /* 'y' */ |
---|
274 | case 0x007a: /* 'z' */ |
---|
275 | retval = char_num; |
---|
276 | break; |
---|
277 | default: |
---|
278 | retval = codepage_valid_char_map[char_num]; |
---|
279 | break; |
---|
280 | } |
---|
281 | } |
---|
282 | else |
---|
283 | retval = char_num; |
---|
284 | |
---|
285 | return CT_LE_W( retval ); |
---|
286 | } |
---|
287 | |
---|
288 | static char |
---|
289 | msdos_get_valid_codepage_filename_character (const uint8_t character) |
---|
290 | { |
---|
291 | return codepage_valid_char_map[(unsigned int)character]; |
---|
292 | } |
---|
293 | |
---|
294 | static ssize_t |
---|
295 | msdos_filename_process_dot_names (const uint8_t *src_name, |
---|
296 | const size_t src_size, |
---|
297 | uint8_t *dest_name, |
---|
298 | const size_t dest_size) |
---|
299 | { |
---|
300 | ssize_t returned_size = 0; |
---|
301 | int eno = 0; |
---|
302 | /* |
---|
303 | * The filenames "." and ".." are handled specially, since they |
---|
304 | * don't follow dos filename rules. |
---|
305 | */ |
---|
306 | if ( src_name[0] == UTF8_FULL_STOP |
---|
307 | && src_size == UTF8_FULL_STOP_SIZE) { |
---|
308 | if (dest_size >= UTF8_FULL_STOP_SIZE) { |
---|
309 | dest_name[0] = UTF8_FULL_STOP; |
---|
310 | returned_size = UTF8_FULL_STOP_SIZE; |
---|
311 | } |
---|
312 | else |
---|
313 | eno = ENAMETOOLONG; |
---|
314 | } |
---|
315 | else if ( eno == 0 |
---|
316 | && src_name[0] == UTF8_FULL_STOP |
---|
317 | && src_name[1] == UTF8_FULL_STOP |
---|
318 | && src_size == ( 2 * UTF8_FULL_STOP_SIZE ) ) { |
---|
319 | if (dest_size >= 2 * UTF8_FULL_STOP_SIZE) { |
---|
320 | dest_name[0] = UTF8_FULL_STOP; |
---|
321 | dest_name[1] = UTF8_FULL_STOP; |
---|
322 | returned_size = 2 * UTF8_FULL_STOP_SIZE; |
---|
323 | } |
---|
324 | else |
---|
325 | eno = ENAMETOOLONG; |
---|
326 | } |
---|
327 | |
---|
328 | if (eno != 0) { |
---|
329 | errno = eno; |
---|
330 | returned_size = -1; |
---|
331 | } |
---|
332 | |
---|
333 | return returned_size; |
---|
334 | } |
---|
335 | |
---|
336 | static ssize_t |
---|
337 | msdos_filename_delete_trailing_dots (const uint8_t *filename_utf8, |
---|
338 | const size_t filename_size) |
---|
339 | { |
---|
340 | ssize_t size_returned = filename_size; |
---|
341 | unsigned int i; |
---|
342 | |
---|
343 | /* |
---|
344 | * Remove any dots from the end of a file name. |
---|
345 | */ |
---|
346 | for ( i = size_returned - UTF8_FULL_STOP_SIZE; |
---|
347 | size_returned >= UTF8_FULL_STOP_SIZE |
---|
348 | && filename_utf8[i] == UTF8_FULL_STOP;) { |
---|
349 | size_returned -= UTF8_FULL_STOP_SIZE; |
---|
350 | i -= UTF8_FULL_STOP_SIZE; |
---|
351 | } |
---|
352 | |
---|
353 | return size_returned; |
---|
354 | } |
---|
355 | |
---|
356 | ssize_t |
---|
357 | msdos_filename_utf8_to_long_name_for_compare ( |
---|
358 | rtems_dosfs_convert_control *converter, |
---|
359 | const uint8_t *utf8_name, |
---|
360 | const size_t utf8_name_size, |
---|
361 | uint8_t *long_name, |
---|
362 | const size_t long_name_size) |
---|
363 | { |
---|
364 | ssize_t returned_size = 0; |
---|
365 | int eno = 0; |
---|
366 | size_t name_size; |
---|
367 | size_t dest_size = long_name_size; |
---|
368 | |
---|
369 | returned_size = msdos_filename_process_dot_names ( |
---|
370 | utf8_name, |
---|
371 | utf8_name_size, |
---|
372 | long_name, |
---|
373 | long_name_size); |
---|
374 | |
---|
375 | if (returned_size == 0) { |
---|
376 | name_size = msdos_filename_delete_trailing_dots ( |
---|
377 | &utf8_name[0], |
---|
378 | utf8_name_size); |
---|
379 | if (name_size > 0) { |
---|
380 | eno = (*converter->handler->utf8_normalize_and_fold) ( |
---|
381 | converter, |
---|
382 | utf8_name, |
---|
383 | name_size, |
---|
384 | long_name, |
---|
385 | &dest_size); |
---|
386 | if (eno == 0) { |
---|
387 | returned_size = (ssize_t)dest_size; |
---|
388 | } |
---|
389 | } else { |
---|
390 | eno = EINVAL; |
---|
391 | } |
---|
392 | } |
---|
393 | |
---|
394 | if ( eno != 0 ) { |
---|
395 | errno = eno; |
---|
396 | returned_size = -1; |
---|
397 | } |
---|
398 | |
---|
399 | return returned_size; |
---|
400 | } |
---|
401 | |
---|
402 | ssize_t |
---|
403 | msdos_filename_utf8_to_long_name_for_save ( |
---|
404 | rtems_dosfs_convert_control *converter, |
---|
405 | const uint8_t *utf8_name, |
---|
406 | const size_t utf8_name_size, |
---|
407 | uint16_t *long_name, |
---|
408 | const size_t long_name_size) |
---|
409 | { |
---|
410 | ssize_t returned_size = 0; |
---|
411 | int eno = 0; |
---|
412 | size_t name_size = utf8_name_size; |
---|
413 | size_t name_size_tmp = long_name_size / MSDOS_NAME_LFN_BYTES_PER_CHAR; |
---|
414 | int i; |
---|
415 | uint16_t c; |
---|
416 | unsigned int chars_written; |
---|
417 | |
---|
418 | name_size_tmp = long_name_size; |
---|
419 | name_size = msdos_filename_delete_trailing_dots ( |
---|
420 | &utf8_name[0], |
---|
421 | utf8_name_size); |
---|
422 | if (name_size > 0) { |
---|
423 | /* |
---|
424 | * Finally convert from UTF-8 to UTF-16 |
---|
425 | */ |
---|
426 | eno = (*converter->handler->utf8_to_utf16) ( |
---|
427 | converter, |
---|
428 | utf8_name, |
---|
429 | name_size, |
---|
430 | &long_name[0], |
---|
431 | &name_size_tmp); |
---|
432 | if (eno == 0) { |
---|
433 | if (name_size_tmp <= (MSDOS_NAME_MAX_LNF_LEN * MSDOS_NAME_LFN_BYTES_PER_CHAR)) |
---|
434 | name_size = name_size_tmp; |
---|
435 | else |
---|
436 | eno = ENAMETOOLONG; |
---|
437 | } |
---|
438 | |
---|
439 | if ( eno == 0 ) |
---|
440 | { |
---|
441 | /* |
---|
442 | * Validate the characters and assign them to the UTF-16 file name |
---|
443 | */ |
---|
444 | for ( i = 0; |
---|
445 | name_size |
---|
446 | && (c = msdos_get_valid_utf16_filename_character ( long_name[i]) ); |
---|
447 | ++i ) { |
---|
448 | long_name[i] = c; |
---|
449 | returned_size += MSDOS_NAME_LFN_BYTES_PER_CHAR; |
---|
450 | name_size -= MSDOS_NAME_LFN_BYTES_PER_CHAR; |
---|
451 | } |
---|
452 | if ( name_size == UTF16_NULL_SIZE && c == UTF16_NULL ) { |
---|
453 | long_name[i] = c; |
---|
454 | returned_size += MSDOS_NAME_LFN_BYTES_PER_CHAR; |
---|
455 | name_size -= MSDOS_NAME_LFN_BYTES_PER_CHAR; |
---|
456 | } |
---|
457 | else if ( name_size != 0 ) |
---|
458 | eno = EINVAL; |
---|
459 | chars_written = returned_size / MSDOS_NAME_LFN_BYTES_PER_CHAR; |
---|
460 | if ( long_name [chars_written - 1] != UTF16_NULL |
---|
461 | && (returned_size + UTF16_NULL_SIZE ) <= long_name_size ) { |
---|
462 | long_name[chars_written] = UTF16_NULL; |
---|
463 | } |
---|
464 | } |
---|
465 | } |
---|
466 | else |
---|
467 | eno = EINVAL; |
---|
468 | |
---|
469 | if ( eno != 0 ) { |
---|
470 | errno = eno; |
---|
471 | returned_size = -1; |
---|
472 | } |
---|
473 | |
---|
474 | return returned_size; |
---|
475 | } |
---|
476 | |
---|
477 | /* |
---|
478 | * Remove any dots from the start of a file name. |
---|
479 | */ |
---|
480 | static void msdos_filename_remove_prepended_dots (const uint8_t **name_utf8, |
---|
481 | size_t *name_size) |
---|
482 | { |
---|
483 | while ( *name_size >= UTF8_FULL_STOP_SIZE |
---|
484 | && **name_utf8 == UTF8_FULL_STOP) { |
---|
485 | *name_utf8 += UTF8_FULL_STOP_SIZE; |
---|
486 | *name_size -= UTF8_FULL_STOP_SIZE; |
---|
487 | } |
---|
488 | } |
---|
489 | |
---|
490 | ssize_t |
---|
491 | msdos_filename_utf8_to_short_name_for_compare ( |
---|
492 | rtems_dosfs_convert_control *converter, |
---|
493 | const uint8_t *utf8_name, |
---|
494 | const size_t utf8_name_size, |
---|
495 | void *short_name, |
---|
496 | const size_t short_name_size) |
---|
497 | { |
---|
498 | ssize_t returned_size = 0; |
---|
499 | int eno = 0; |
---|
500 | const uint8_t *name_ptr = utf8_name; |
---|
501 | char *dest_ptr = (char*)short_name; |
---|
502 | size_t name_size = utf8_name_size; |
---|
503 | uint8_t name_normalized_buf[(MSDOS_SHORT_NAME_LEN +1) * MSDOS_NAME_MAX_UTF8_BYTES_PER_CHAR]; |
---|
504 | size_t name_size_tmp = sizeof(name_normalized_buf); |
---|
505 | |
---|
506 | returned_size = msdos_filename_process_dot_names ( |
---|
507 | utf8_name, |
---|
508 | utf8_name_size, |
---|
509 | short_name, |
---|
510 | short_name_size); |
---|
511 | |
---|
512 | if (returned_size == 0) { |
---|
513 | msdos_filename_remove_prepended_dots (&name_ptr, |
---|
514 | &name_size); |
---|
515 | if (name_size > 0) { |
---|
516 | /* |
---|
517 | * Normalize the name and convert to lower case |
---|
518 | */ |
---|
519 | eno = (*converter->handler->utf8_normalize_and_fold) ( |
---|
520 | converter, |
---|
521 | name_ptr, |
---|
522 | name_size, |
---|
523 | &name_normalized_buf[0], |
---|
524 | &name_size_tmp); |
---|
525 | name_ptr = &name_normalized_buf[0]; |
---|
526 | name_size = name_size_tmp; |
---|
527 | if ( eno == ENOMEM ) { |
---|
528 | eno = 0; |
---|
529 | } |
---|
530 | if ( eno == 0 ) { |
---|
531 | memcpy (&dest_ptr[0], &name_ptr[0], name_size); |
---|
532 | returned_size = name_size; |
---|
533 | } |
---|
534 | } else |
---|
535 | eno = EINVAL; |
---|
536 | } |
---|
537 | |
---|
538 | if ( eno != 0 ) { |
---|
539 | errno = eno; |
---|
540 | returned_size = -1; |
---|
541 | } |
---|
542 | |
---|
543 | return returned_size; |
---|
544 | } |
---|
545 | |
---|
546 | ssize_t |
---|
547 | msdos_filename_utf8_to_short_name_for_save ( |
---|
548 | rtems_dosfs_convert_control *converter, |
---|
549 | const uint8_t *utf8_name, |
---|
550 | const size_t utf8_name_size, |
---|
551 | void *short_name, |
---|
552 | const size_t short_name_size) |
---|
553 | { |
---|
554 | ssize_t returned_size = 0; |
---|
555 | int eno = 0; |
---|
556 | const uint8_t *name_ptr = utf8_name; |
---|
557 | size_t name_size = utf8_name_size; |
---|
558 | char *dest_ptr = (char*)short_name; |
---|
559 | unsigned int i; |
---|
560 | char c; |
---|
561 | size_t name_size_tmp; |
---|
562 | char name_to_format_buf[MSDOS_SHORT_NAME_LEN +1]; |
---|
563 | |
---|
564 | returned_size = msdos_filename_process_dot_names ( |
---|
565 | utf8_name, |
---|
566 | utf8_name_size, |
---|
567 | short_name, |
---|
568 | short_name_size); |
---|
569 | |
---|
570 | if (returned_size == 0) { |
---|
571 | msdos_filename_remove_prepended_dots (&name_ptr, |
---|
572 | &name_size); |
---|
573 | |
---|
574 | if (name_size > 0) { |
---|
575 | /* |
---|
576 | * Finally convert from UTF-8 to codepage |
---|
577 | */ |
---|
578 | name_size_tmp = sizeof ( name_to_format_buf ); |
---|
579 | eno = (*converter->handler->utf8_to_codepage) ( |
---|
580 | converter, |
---|
581 | name_ptr, |
---|
582 | name_size, |
---|
583 | &name_to_format_buf[0], |
---|
584 | &name_size_tmp); |
---|
585 | if ( eno != 0 ) { |
---|
586 | /* The UTF-8 name my well be long name, for which we now want to |
---|
587 | * generate the corresponding short name. Under these circumstances |
---|
588 | * eno != 0 likely simply means that the UTF-8 name is longer than 11 characters |
---|
589 | * or that it contains unicode characters which can not be converted to the code page |
---|
590 | * in a reversible way. Non-reversible characters will be represented by question mark |
---|
591 | * characters. Later in this method they will get replaced by underline characters. |
---|
592 | */ |
---|
593 | eno = 0; |
---|
594 | } |
---|
595 | name_ptr = (const uint8_t *)(&name_to_format_buf[0]); |
---|
596 | name_size = name_size_tmp; |
---|
597 | for (i = 0; i < name_size; ++i) |
---|
598 | name_to_format_buf[i] = toupper ( (unsigned char)(name_to_format_buf[i]) ); |
---|
599 | /* |
---|
600 | * Validate the characters and assign them to the codepage file name |
---|
601 | */ |
---|
602 | if ( name_size > 0 ) { |
---|
603 | /* |
---|
604 | * The first character needs some special treatment |
---|
605 | */ |
---|
606 | if ( 0x20 == *name_ptr ) |
---|
607 | dest_ptr[0] = '_'; |
---|
608 | else if ( 0xE5 == *name_ptr ) |
---|
609 | dest_ptr[0] = 0x05; |
---|
610 | else if (0 != (c = msdos_get_valid_codepage_filename_character( *name_ptr ) ) ) |
---|
611 | dest_ptr[0] = c; |
---|
612 | else |
---|
613 | dest_ptr[0] = '_'; |
---|
614 | ++name_ptr; |
---|
615 | ++returned_size; |
---|
616 | --name_size; |
---|
617 | /* |
---|
618 | * Validate and assign all other characters of the name part |
---|
619 | */ |
---|
620 | for (i = 1; i <= 7 && name_size && *name_ptr != '.'; ++i) { |
---|
621 | c = msdos_get_valid_codepage_filename_character ( *name_ptr ); |
---|
622 | if (c != 0) |
---|
623 | dest_ptr[i] = c; |
---|
624 | else |
---|
625 | dest_ptr[i] = '_'; |
---|
626 | ++name_ptr; |
---|
627 | ++returned_size; |
---|
628 | --name_size; |
---|
629 | } |
---|
630 | /* |
---|
631 | * Strip any further characters up to a '.' or the end of the |
---|
632 | * string. |
---|
633 | */ |
---|
634 | if ( *name_ptr == '.' ) { |
---|
635 | ++name_ptr; |
---|
636 | --name_size; |
---|
637 | } |
---|
638 | |
---|
639 | for (; i < 8; ++i) { |
---|
640 | dest_ptr[i] = ' '; |
---|
641 | ++returned_size; |
---|
642 | } |
---|
643 | |
---|
644 | /* |
---|
645 | * Copy in the extension part of the name, if any. |
---|
646 | */ |
---|
647 | for (; i <= 10 && name_size ; i++) { |
---|
648 | c = msdos_get_valid_codepage_filename_character ( *name_ptr); |
---|
649 | if (c != 0) |
---|
650 | dest_ptr[i] = c; |
---|
651 | else |
---|
652 | dest_ptr[i] = '_'; |
---|
653 | ++name_ptr; |
---|
654 | ++returned_size; |
---|
655 | name_size--; |
---|
656 | } |
---|
657 | /* |
---|
658 | * Fill up with blanks. These are DOS's pad characters. |
---|
659 | */ |
---|
660 | for ( ; i < short_name_size; ++i ) { |
---|
661 | dest_ptr[i] = ' '; |
---|
662 | ++returned_size; |
---|
663 | } |
---|
664 | } |
---|
665 | } |
---|
666 | else |
---|
667 | eno = EINVAL; |
---|
668 | } |
---|
669 | |
---|
670 | if ( eno != 0 ) { |
---|
671 | errno = eno; |
---|
672 | return -1; |
---|
673 | } |
---|
674 | |
---|
675 | return returned_size; |
---|
676 | } |
---|
677 | |
---|
678 | |
---|