[RTL]
[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 */
229 BOOLEAN
230 NTAPI
231 RtlIsNameLegalDOS8Dot3(IN PCUNICODE_STRING UnicodeName,
232 IN OUT POEM_STRING AnsiName OPTIONAL,
233 IN OUT PBOOLEAN SpacesFound OPTIONAL)
234 {
235 static const char Illegal[] = "*?<>|\"+=,;[]:/\\\345";
236 int Dot = -1;
237 int i;
238 char Buffer[12];
239 OEM_STRING OemString;
240 BOOLEAN GotSpace = FALSE;
241 NTSTATUS Status;
242
243 if (SpacesFound) *SpacesFound = FALSE;
244
245 if (!AnsiName)
246 {
247 OemString.Length = sizeof(Buffer);
248 OemString.MaximumLength = sizeof(Buffer);
249 OemString.Buffer = Buffer;
250 AnsiName = &OemString;
251 }
252
253 Status = RtlUpcaseUnicodeStringToCountedOemString(AnsiName, UnicodeName, FALSE);
254 if (!NT_SUCCESS(Status))
255 return FALSE;
256
257 if ((AnsiName->Length > 12) || (AnsiName->Buffer == NULL)) return FALSE;
258
259 /* a starting . is invalid, except for . and .. */
260 if (AnsiName->Buffer[0] == '.')
261 {
262 if (AnsiName->Length != 1 && (AnsiName->Length != 2 || AnsiName->Buffer[1] != '.')) return FALSE;
263 return TRUE;
264 }
265
266 for (i = 0; i < AnsiName->Length; i++)
267 {
268 switch (AnsiName->Buffer[i])
269 {
270 case ' ':
271 /* leading/trailing spaces not allowed */
272 if (!i || i == AnsiName->Length-1 || AnsiName->Buffer[i+1] == '.') return FALSE;
273 GotSpace = TRUE;
274 break;
275 case '.':
276 if (Dot != -1) return FALSE;
277 Dot = i;
278 break;
279 default:
280 if (strchr(Illegal, AnsiName->Buffer[i])) return FALSE;
281 break;
282 }
283 }
284 /* check file part is shorter than 8, extension shorter than 3
285 * dot cannot be last in string
286 */
287 if (Dot == -1)
288 {
289 if (AnsiName->Length > 8) return FALSE;
290 }
291 else
292 {
293 if (Dot > 8 || (AnsiName->Length - Dot > 4) || Dot == AnsiName->Length - 1) return FALSE;
294 }
295 if (SpacesFound) *SpacesFound = GotSpace;
296 return TRUE;
297 }
298
299 /* EOF */