[RTL]
[reactos.git] / reactos / lib / rtl / dos8dot3.c
1 /*
2 * COPYRIGHT: See COPYING in the top level directory
3 * PROJECT: ReactOS system libraries
4 * FILE: lib/rtl/dos8dot3.c
5 * PURPOSE: Short name (8.3 name) functions
6 * PROGRAMMER: Eric Kohl
7 */
8
9 /* INCLUDES ******************************************************************/
10
11 #include <rtl.h>
12
13 #define NDEBUG
14 #include <debug.h>
15
16
17 /* CONSTANTS *****************************************************************/
18
19 const PCHAR RtlpShortIllegals = ";+=[],\"*\\<>/?:|";
20
21
22 /* FUNCTIONS *****************************************************************/
23
24 static BOOLEAN
25 RtlpIsShortIllegal(CHAR Char)
26 {
27 return strchr(RtlpShortIllegals, Char) ? TRUE : FALSE;
28 }
29
30 static USHORT
31 RtlpGetCheckSum(PUNICODE_STRING Name)
32 {
33 USHORT Hash, Saved;
34 WCHAR * CurChar;
35 USHORT Len;
36
37 if (Name->Length == 0)
38 {
39 return 0;
40 }
41
42 if (Name->Length == sizeof(WCHAR))
43 {
44 return Name->Buffer[0];
45 }
46
47 CurChar = Name->Buffer;
48 Hash = (*CurChar << 8) + *(CurChar + 1);
49 if (Name->Length == 2 * sizeof(WCHAR))
50 {
51 return Hash;
52 }
53
54 Saved = Hash;
55 Len = 2;
56 do
57 {
58 CurChar = CurChar + 2;
59 Hash = (Hash << 7) + *CurChar;
60 Hash = (Saved >> 1) + (Hash << 8);
61 if (Len + 1 < Name->Length / sizeof(WCHAR))
62 {
63 Hash += *(CurChar + 1);
64 }
65 Saved = Hash;
66 Len += 2;
67 } while (Len < Name->Length / sizeof(WCHAR));
68
69 return Hash;
70 }
71
72 static ULONG
73 RtlpGetIndexLength(ULONG Index)
74 {
75 ULONG Length = 0;
76 while (Index)
77 {
78 Index /= 10;
79 Length++;
80 }
81 return Length ? Length : 1;
82 }
83
84
85 /*
86 * @implemented
87 */
88 VOID NTAPI
89 RtlGenerate8dot3Name(IN PUNICODE_STRING Name,
90 IN BOOLEAN AllowExtendedCharacters,
91 IN OUT PGENERATE_NAME_CONTEXT Context,
92 OUT PUNICODE_STRING Name8dot3)
93 {
94 ULONG Count;
95 WCHAR NameBuffer[8];
96 WCHAR ExtBuffer[4];
97 ULONG StrLength;
98 ULONG NameLength;
99 ULONG ExtLength;
100 ULONG CopyLength;
101 ULONG DotPos;
102 ULONG i, j;
103 ULONG IndexLength;
104 ULONG CurrentIndex;
105 USHORT Checksum;
106 WCHAR c;
107
108 StrLength = Name->Length / sizeof(WCHAR);
109 DPRINT("StrLength: %lu\n", StrLength);
110
111 /* Find last dot in Name */
112 DotPos = StrLength;
113 for (i = 0; i < StrLength; i++)
114 {
115 if (Name->Buffer[i] == L'.')
116 {
117 DotPos = i;
118 }
119 }
120
121 DPRINT("DotPos: %lu\n", DotPos);
122
123 /* Copy name (6 valid characters max) */
124 for (i = 0, NameLength = 0; NameLength < 6 && i < DotPos; i++)
125 {
126 c = UNICODE_NULL;
127 if (AllowExtendedCharacters)
128 {
129 c = RtlUpcaseUnicodeChar(Name->Buffer[i]);
130 Count = 1;
131 }
132 else
133 {
134 RtlUpcaseUnicodeToOemN((CHAR *)&c, sizeof(CHAR), &Count, &Name->Buffer[i], sizeof(WCHAR));
135 }
136
137 if (Count != 1 || c == UNICODE_NULL || RtlpIsShortIllegal(c))
138 {
139 NameBuffer[NameLength++] = L'_';
140 }
141 else if (c != L'.' && c != L' ')
142 {
143 if (isgraph(c) || (AllowExtendedCharacters && iswgraph(c)))
144 {
145 NameBuffer[NameLength++] = c;
146 }
147 }
148 }
149
150 DPRINT("NameBuffer: '%.08S'\n", NameBuffer);
151 DPRINT("NameLength: %lu\n", NameLength);
152
153 /* Copy extension (4 valid characters max) */
154 if (DotPos < StrLength)
155 {
156 for (i = DotPos, ExtLength = 0; ExtLength < 4 && i < StrLength; i++)
157 {
158 c = UNICODE_NULL;
159 if (AllowExtendedCharacters)
160 {
161 c = RtlUpcaseUnicodeChar(Name->Buffer[i]);
162 Count = 1;
163 }
164 else
165 {
166 RtlUpcaseUnicodeToOemN((CHAR *)&c, sizeof(CHAR), &Count, &Name->Buffer[i], sizeof(WCHAR));
167 }
168
169 if (Count != 1 || c == UNICODE_NULL || RtlpIsShortIllegal(c))
170 {
171 ExtBuffer[ExtLength++] = L'_';
172 }
173 else if (c != L' ')
174 {
175 if (isgraph(c) || c == L'.' || (AllowExtendedCharacters && iswgraph(c)))
176 {
177 ExtBuffer[ExtLength++] = c;
178 }
179 }
180 }
181 }
182 else
183 {
184 ExtLength = 0;
185 }
186 DPRINT("ExtBuffer: '%.04S'\n", ExtBuffer);
187 DPRINT("ExtLength: %lu\n", ExtLength);
188
189 /* Determine next index */
190 IndexLength = RtlpGetIndexLength(Context->LastIndexValue);
191 if (Context->CheckSumInserted)
192 {
193 CopyLength = min(NameLength, 8 - 4 - 1 - IndexLength);
194 Checksum = RtlpGetCheckSum(Name);
195 }
196 else
197 {
198 CopyLength = min(NameLength, 8 - 1 - IndexLength);
199 Checksum = 0;
200 }
201
202 DPRINT("CopyLength: %lu\n", CopyLength);
203
204 if ((Context->NameLength == CopyLength) &&
205 (wcsncmp(Context->NameBuffer, NameBuffer, CopyLength) == 0) &&
206 (Context->ExtensionLength == ExtLength) &&
207 (wcsncmp(Context->ExtensionBuffer, ExtBuffer, ExtLength) == 0) &&
208 (Checksum == Context->Checksum) &&
209 (Context->LastIndexValue < 999))
210 {
211 Context->LastIndexValue++;
212 if (Context->CheckSumInserted == FALSE &&
213 Context->LastIndexValue > 9)
214 {
215 Context->CheckSumInserted = TRUE;
216 Context->LastIndexValue = 1;
217 Context->Checksum = RtlpGetCheckSum(Name);
218 }
219 }
220 else
221 {
222 Context->LastIndexValue = 1;
223 if (NameLength == 0)
224 {
225 Context->CheckSumInserted = TRUE;
226 Context->Checksum = RtlpGetCheckSum(Name);
227 }
228 else
229 {
230 Context->CheckSumInserted = FALSE;
231 }
232 }
233
234 IndexLength = RtlpGetIndexLength(Context->LastIndexValue);
235
236 DPRINT("CurrentIndex: %lu, IndexLength %lu\n", Context->LastIndexValue, IndexLength);
237
238 if (Context->CheckSumInserted)
239 {
240 CopyLength = min(NameLength, 8 - 4 - 1 - IndexLength);
241 }
242 else
243 {
244 CopyLength = min(NameLength, 8 - 1 - IndexLength);
245 }
246
247 /* Build the short name */
248 memcpy(Name8dot3->Buffer, NameBuffer, CopyLength * sizeof(WCHAR));
249 j = CopyLength;
250 if (Context->CheckSumInserted)
251 {
252 Checksum = Context->Checksum;
253 for (i = 0; i < 4; i++)
254 {
255 Name8dot3->Buffer[j++] = (Checksum & 0xF) > 9 ? (Checksum & 0xF) + L'A' - 10 : (Checksum & 0xF) + L'0';
256 Checksum >>= 4;
257 }
258 j = CopyLength + 4;
259 }
260 Name8dot3->Buffer[j++] = L'~';
261 j += IndexLength - 1;
262 CurrentIndex = Context->LastIndexValue;
263 for (i = 0; i < IndexLength; i++)
264 {
265 Name8dot3->Buffer[j--] = (CurrentIndex % 10) + L'0';
266 CurrentIndex /= 10;
267 }
268 j += IndexLength + 1;
269
270 memcpy(Name8dot3->Buffer + j, ExtBuffer, ExtLength * sizeof(WCHAR));
271 Name8dot3->Length = (USHORT)(j + ExtLength) * sizeof(WCHAR);
272
273 DPRINT("Name8dot3: '%wZ'\n", Name8dot3);
274
275 /* Update context */
276 Context->NameLength = (UCHAR)CopyLength;
277 Context->ExtensionLength = ExtLength;
278 memcpy(Context->NameBuffer, NameBuffer, CopyLength * sizeof(WCHAR));
279 memcpy(Context->ExtensionBuffer, ExtBuffer, ExtLength * sizeof(WCHAR));
280 }
281
282
283 /*
284 * @implemented
285 * Note: the function does not conform to the annotations.
286 * SpacesFound is not always set!
287 */
288 _IRQL_requires_max_(PASSIVE_LEVEL)
289 _Must_inspect_result_
290 NTSYSAPI
291 BOOLEAN
292 NTAPI
293 RtlIsNameLegalDOS8Dot3 (
294 _In_ PCUNICODE_STRING Name,
295 _Inout_opt_ POEM_STRING OemName,
296 _Out_opt_ PBOOLEAN NameContainsSpaces)
297 {
298 static const char Illegal[] = "*?<>|\"+=,;[]:/\\\345";
299 int Dot = -1;
300 int i;
301 char Buffer[12];
302 OEM_STRING OemString;
303 BOOLEAN GotSpace = FALSE;
304 NTSTATUS Status;
305
306 if (!OemName)
307 {
308 OemString.Length = sizeof(Buffer);
309 OemString.MaximumLength = sizeof(Buffer);
310 OemString.Buffer = Buffer;
311 OemName = &OemString;
312 }
313
314 Status = RtlUpcaseUnicodeStringToCountedOemString(OemName, Name, FALSE);
315 if (!NT_SUCCESS(Status))
316 return FALSE;
317
318 if ((OemName->Length > 12) || (OemName->Buffer == NULL)) return FALSE;
319
320 /* a starting . is invalid, except for . and .. */
321 if (OemName->Buffer[0] == '.')
322 {
323 if (OemName->Length != 1 && (OemName->Length != 2 || OemName->Buffer[1] != '.')) return FALSE;
324 if (NameContainsSpaces) *NameContainsSpaces = FALSE;
325 return TRUE;
326 }
327
328 for (i = 0; i < OemName->Length; i++)
329 {
330 switch (OemName->Buffer[i])
331 {
332 case ' ':
333 /* leading/trailing spaces not allowed */
334 if (!i || i == OemName->Length-1 || OemName->Buffer[i+1] == '.') return FALSE;
335 GotSpace = TRUE;
336 break;
337 case '.':
338 if (Dot != -1) return FALSE;
339 Dot = i;
340 break;
341 default:
342 if (strchr(Illegal, OemName->Buffer[i])) return FALSE;
343 break;
344 }
345 }
346 /* check file part is shorter than 8, extension shorter than 3
347 * dot cannot be last in string
348 */
349 if (Dot == -1)
350 {
351 if (OemName->Length > 8) return FALSE;
352 }
353 else
354 {
355 if (Dot > 8 || (OemName->Length - Dot > 4) || Dot == OemName->Length - 1) return FALSE;
356 }
357 if (NameContainsSpaces) *NameContainsSpaces = GotSpace;
358 return TRUE;
359 }
360
361 /* EOF */