145dce90de6c93b15d52e559f1db1bd3d2e16983
[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 = 0;
34 ULONG Length;
35 PWCHAR c;
36
37 Length = Name->Length / sizeof(WCHAR);
38 c = Name->Buffer;
39 while(Length--)
40 {
41 Hash = (Hash + (*c << 4) + (*c >> 4)) * 11;
42 c++;
43 }
44 return Hash;
45 }
46
47 static ULONG
48 RtlpGetIndexLength(ULONG Index)
49 {
50 ULONG Length = 0;
51 while (Index)
52 {
53 Index /= 10;
54 Length++;
55 }
56 return Length ? Length : 1;
57 }
58
59
60 /*
61 * @implemented
62 */
63 VOID NTAPI
64 RtlGenerate8dot3Name(IN PUNICODE_STRING Name,
65 IN BOOLEAN AllowExtendedCharacters,
66 IN OUT PGENERATE_NAME_CONTEXT Context,
67 OUT PUNICODE_STRING Name8dot3)
68 {
69 ULONG Count;
70 WCHAR NameBuffer[8];
71 WCHAR ExtBuffer[4];
72 ULONG StrLength;
73 ULONG NameLength;
74 ULONG ExtLength;
75 ULONG CopyLength;
76 ULONG DotPos;
77 ULONG i, j;
78 ULONG IndexLength;
79 ULONG CurrentIndex;
80 USHORT Checksum;
81 CHAR c;
82
83 StrLength = Name->Length / sizeof(WCHAR);
84 DPRINT("StrLength: %lu\n", StrLength);
85
86 /* Find last dot in Name */
87 DotPos = StrLength;
88 for (i = 0; i < StrLength; i++)
89 {
90 if (Name->Buffer[i] == L'.')
91 {
92 DotPos = i;
93 }
94 }
95
96 DPRINT("DotPos: %lu\n", DotPos);
97
98 /* Copy name (6 valid characters max) */
99 for (i = 0, NameLength = 0; NameLength < 6 && i < DotPos; i++)
100 {
101 c = 0;
102 RtlUpcaseUnicodeToOemN(&c, sizeof(CHAR), &Count, &Name->Buffer[i], sizeof(WCHAR));
103 if (Count != 1 || c == 0 || RtlpIsShortIllegal(c))
104 {
105 NameBuffer[NameLength++] = L'_';
106 }
107 else if (c != '.' && c != ' ')
108 {
109 NameBuffer[NameLength++] = (WCHAR)c;
110 }
111 }
112
113 DPRINT("NameBuffer: '%.08S'\n", NameBuffer);
114 DPRINT("NameLength: %lu\n", NameLength);
115
116 /* Copy extension (4 valid characters max) */
117 if (DotPos < StrLength)
118 {
119 for (i = DotPos, ExtLength = 0; ExtLength < 4 && i < StrLength; i++)
120 {
121 c = 0;
122 RtlUpcaseUnicodeToOemN(&c, sizeof(CHAR), &Count, &Name->Buffer[i], sizeof(WCHAR));
123 if (Count != 1 || c == 0 || RtlpIsShortIllegal(c))
124 {
125 ExtBuffer[ExtLength++] = L'_';
126 }
127 else if (c != ' ')
128 {
129 ExtBuffer[ExtLength++] = c;
130 }
131 }
132 }
133 else
134 {
135 ExtLength = 0;
136 }
137 DPRINT("ExtBuffer: '%.04S'\n", ExtBuffer);
138 DPRINT("ExtLength: %lu\n", ExtLength);
139
140 /* Determine next index */
141 IndexLength = RtlpGetIndexLength(Context->LastIndexValue);
142 if (Context->CheckSumInserted)
143 {
144 CopyLength = min(NameLength, 8 - 4 - 1 - IndexLength);
145 Checksum = RtlpGetCheckSum(Name);
146 }
147 else
148 {
149 CopyLength = min(NameLength, 8 - 1 - IndexLength);
150 Checksum = 0;
151 }
152
153 DPRINT("CopyLength: %lu\n", CopyLength);
154
155 if ((Context->NameLength == CopyLength) &&
156 (wcsncmp(Context->NameBuffer, NameBuffer, CopyLength) == 0) &&
157 (Context->ExtensionLength == ExtLength) &&
158 (wcsncmp(Context->ExtensionBuffer, ExtBuffer, ExtLength) == 0) &&
159 (Checksum == Context->Checksum) &&
160 (Context->LastIndexValue < 999))
161 {
162 Context->LastIndexValue++;
163 if (Context->CheckSumInserted == FALSE &&
164 Context->LastIndexValue > 9)
165 {
166 Context->CheckSumInserted = TRUE;
167 Context->LastIndexValue = 1;
168 Context->Checksum = RtlpGetCheckSum(Name);
169 }
170 }
171 else
172 {
173 Context->LastIndexValue = 1;
174 Context->CheckSumInserted = FALSE;
175 }
176
177 IndexLength = RtlpGetIndexLength(Context->LastIndexValue);
178
179 DPRINT("CurrentIndex: %lu, IndexLength %lu\n", Context->LastIndexValue, IndexLength);
180
181 if (Context->CheckSumInserted)
182 {
183 CopyLength = min(NameLength, 8 - 4 - 1 - IndexLength);
184 }
185 else
186 {
187 CopyLength = min(NameLength, 8 - 1 - IndexLength);
188 }
189
190 /* Build the short name */
191 memcpy(Name8dot3->Buffer, NameBuffer, CopyLength * sizeof(WCHAR));
192 j = CopyLength;
193 if (Context->CheckSumInserted)
194 {
195 j += 3;
196 Checksum = Context->Checksum;
197 for (i = 0; i < 4; i++)
198 {
199 Name8dot3->Buffer[j--] = (Checksum % 16) > 9 ? (Checksum % 16) + L'A' - 10 : (Checksum % 16) + L'0';
200 Checksum /= 16;
201 }
202 j = CopyLength + 4;
203 }
204 Name8dot3->Buffer[j++] = L'~';
205 j += IndexLength - 1;
206 CurrentIndex = Context->LastIndexValue;
207 for (i = 0; i < IndexLength; i++)
208 {
209 Name8dot3->Buffer[j--] = (CurrentIndex % 10) + L'0';
210 CurrentIndex /= 10;
211 }
212 j += IndexLength + 1;
213
214 memcpy(Name8dot3->Buffer + j, ExtBuffer, ExtLength * sizeof(WCHAR));
215 Name8dot3->Length = (USHORT)(j + ExtLength) * sizeof(WCHAR);
216
217 DPRINT("Name8dot3: '%wZ'\n", Name8dot3);
218
219 /* Update context */
220 Context->NameLength = (UCHAR)CopyLength;
221 Context->ExtensionLength = ExtLength;
222 memcpy(Context->NameBuffer, NameBuffer, CopyLength * sizeof(WCHAR));
223 memcpy(Context->ExtensionBuffer, ExtBuffer, ExtLength * sizeof(WCHAR));
224 }
225
226
227 /*
228 * @implemented
229 * Note: the function does not conform to the annotations.
230 * SpacesFound is not always set!
231 */
232 _IRQL_requires_max_(PASSIVE_LEVEL)
233 _Must_inspect_result_
234 NTSYSAPI
235 BOOLEAN
236 NTAPI
237 RtlIsNameLegalDOS8Dot3 (
238 _In_ PCUNICODE_STRING Name,
239 _Inout_opt_ POEM_STRING OemName,
240 _Out_opt_ PBOOLEAN NameContainsSpaces)
241 {
242 static const char Illegal[] = "*?<>|\"+=,;[]:/\\\345";
243 int Dot = -1;
244 int i;
245 char Buffer[12];
246 OEM_STRING OemString;
247 BOOLEAN GotSpace = FALSE;
248 NTSTATUS Status;
249
250 if (!OemName)
251 {
252 OemString.Length = sizeof(Buffer);
253 OemString.MaximumLength = sizeof(Buffer);
254 OemString.Buffer = Buffer;
255 OemName = &OemString;
256 }
257
258 Status = RtlUpcaseUnicodeStringToCountedOemString(OemName, Name, FALSE);
259 if (!NT_SUCCESS(Status))
260 return FALSE;
261
262 if ((OemName->Length > 12) || (OemName->Buffer == NULL)) return FALSE;
263
264 /* a starting . is invalid, except for . and .. */
265 if (OemName->Buffer[0] == '.')
266 {
267 if (OemName->Length != 1 && (OemName->Length != 2 || OemName->Buffer[1] != '.')) return FALSE;
268 if (NameContainsSpaces) *NameContainsSpaces = FALSE;
269 return TRUE;
270 }
271
272 for (i = 0; i < OemName->Length; i++)
273 {
274 switch (OemName->Buffer[i])
275 {
276 case ' ':
277 /* leading/trailing spaces not allowed */
278 if (!i || i == OemName->Length-1 || OemName->Buffer[i+1] == '.') return FALSE;
279 GotSpace = TRUE;
280 break;
281 case '.':
282 if (Dot != -1) return FALSE;
283 Dot = i;
284 break;
285 default:
286 if (strchr(Illegal, OemName->Buffer[i])) return FALSE;
287 break;
288 }
289 }
290 /* check file part is shorter than 8, extension shorter than 3
291 * dot cannot be last in string
292 */
293 if (Dot == -1)
294 {
295 if (OemName->Length > 8) return FALSE;
296 }
297 else
298 {
299 if (Dot > 8 || (OemName->Length - Dot > 4) || Dot == OemName->Length - 1) return FALSE;
300 }
301 if (NameContainsSpaces) *NameContainsSpaces = GotSpace;
302 return TRUE;
303 }
304
305 /* EOF */