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