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
9 /* INCLUDES ******************************************************************/
17 /* CONSTANTS *****************************************************************/
19 const PCHAR RtlpShortIllegals
= ";+=[],\"*\\<>/?:|";
22 /* FUNCTIONS *****************************************************************/
25 RtlpIsShortIllegal(CHAR Char
)
27 return strchr(RtlpShortIllegals
, Char
) ? TRUE
: FALSE
;
31 RtlpGetCheckSum(PUNICODE_STRING Name
)
37 if (Name
->Length
== 0)
42 if (Name
->Length
== sizeof(WCHAR
))
44 return Name
->Buffer
[0];
47 CurChar
= Name
->Buffer
;
48 Hash
= (*CurChar
<< 8) + *(CurChar
+ 1);
49 if (Name
->Length
== 2 * sizeof(WCHAR
))
58 CurChar
= CurChar
+ 2;
59 Hash
= (Hash
<< 7) + *CurChar
;
60 Hash
= (Saved
>> 1) + (Hash
<< 8);
61 if (Len
+ 1 < Name
->Length
/ sizeof(WCHAR
))
63 Hash
+= *(CurChar
+ 1);
67 } while (Len
< Name
->Length
/ sizeof(WCHAR
));
73 RtlpGetIndexLength(ULONG Index
)
81 return Length
? Length
: 1;
89 RtlGenerate8dot3Name(IN PUNICODE_STRING Name
,
90 IN BOOLEAN AllowExtendedCharacters
,
91 IN OUT PGENERATE_NAME_CONTEXT Context
,
92 OUT PUNICODE_STRING Name8dot3
)
108 StrLength
= Name
->Length
/ sizeof(WCHAR
);
109 DPRINT("StrLength: %lu\n", StrLength
);
111 /* Find last dot in Name */
113 for (i
= 0; i
< StrLength
; i
++)
115 if (Name
->Buffer
[i
] == L
'.')
121 DPRINT("DotPos: %lu\n", DotPos
);
123 /* Copy name (6 valid characters max) */
124 for (i
= 0, NameLength
= 0; NameLength
< 6 && i
< DotPos
; i
++)
127 if (AllowExtendedCharacters
)
129 c
= RtlUpcaseUnicodeChar(Name
->Buffer
[i
]);
134 RtlUpcaseUnicodeToOemN((CHAR
*)&c
, sizeof(CHAR
), &Count
, &Name
->Buffer
[i
], sizeof(WCHAR
));
137 if (Count
!= 1 || c
== UNICODE_NULL
|| RtlpIsShortIllegal(c
))
139 NameBuffer
[NameLength
++] = L
'_';
141 else if (c
!= L
'.' && c
!= L
' ')
143 if (isgraph(c
) || (AllowExtendedCharacters
&& iswgraph(c
)))
145 NameBuffer
[NameLength
++] = c
;
150 DPRINT("NameBuffer: '%.08S'\n", NameBuffer
);
151 DPRINT("NameLength: %lu\n", NameLength
);
153 /* Copy extension (4 valid characters max) */
154 if (DotPos
< StrLength
)
156 for (i
= DotPos
, ExtLength
= 0; ExtLength
< 4 && i
< StrLength
; i
++)
159 if (AllowExtendedCharacters
)
161 c
= RtlUpcaseUnicodeChar(Name
->Buffer
[i
]);
166 RtlUpcaseUnicodeToOemN((CHAR
*)&c
, sizeof(CHAR
), &Count
, &Name
->Buffer
[i
], sizeof(WCHAR
));
169 if (Count
!= 1 || c
== UNICODE_NULL
|| RtlpIsShortIllegal(c
))
171 ExtBuffer
[ExtLength
++] = L
'_';
175 if (isgraph(c
) || c
== L
'.' || (AllowExtendedCharacters
&& iswgraph(c
)))
177 ExtBuffer
[ExtLength
++] = c
;
186 DPRINT("ExtBuffer: '%.04S'\n", ExtBuffer
);
187 DPRINT("ExtLength: %lu\n", ExtLength
);
189 /* Determine next index */
190 IndexLength
= RtlpGetIndexLength(Context
->LastIndexValue
);
191 if (Context
->CheckSumInserted
)
193 CopyLength
= min(NameLength
, 8 - 4 - 1 - IndexLength
);
194 Checksum
= RtlpGetCheckSum(Name
);
198 CopyLength
= min(NameLength
, 8 - 1 - IndexLength
);
202 DPRINT("CopyLength: %lu\n", CopyLength
);
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))
211 Context
->LastIndexValue
++;
212 if (Context
->CheckSumInserted
== FALSE
&&
213 Context
->LastIndexValue
> 9)
215 Context
->CheckSumInserted
= TRUE
;
216 Context
->LastIndexValue
= 1;
217 Context
->Checksum
= RtlpGetCheckSum(Name
);
222 Context
->LastIndexValue
= 1;
225 Context
->CheckSumInserted
= TRUE
;
226 Context
->Checksum
= RtlpGetCheckSum(Name
);
230 Context
->CheckSumInserted
= FALSE
;
234 IndexLength
= RtlpGetIndexLength(Context
->LastIndexValue
);
236 DPRINT("CurrentIndex: %lu, IndexLength %lu\n", Context
->LastIndexValue
, IndexLength
);
238 if (Context
->CheckSumInserted
)
240 CopyLength
= min(NameLength
, 8 - 4 - 1 - IndexLength
);
244 CopyLength
= min(NameLength
, 8 - 1 - IndexLength
);
247 /* Build the short name */
248 memcpy(Name8dot3
->Buffer
, NameBuffer
, CopyLength
* sizeof(WCHAR
));
250 if (Context
->CheckSumInserted
)
252 Checksum
= Context
->Checksum
;
253 for (i
= 0; i
< 4; i
++)
255 Name8dot3
->Buffer
[j
++] = (Checksum
& 0xF) > 9 ? (Checksum
& 0xF) + L
'A' - 10 : (Checksum
& 0xF) + L
'0';
260 Name8dot3
->Buffer
[j
++] = L
'~';
261 j
+= IndexLength
- 1;
262 CurrentIndex
= Context
->LastIndexValue
;
263 for (i
= 0; i
< IndexLength
; i
++)
265 Name8dot3
->Buffer
[j
--] = (CurrentIndex
% 10) + L
'0';
268 j
+= IndexLength
+ 1;
270 memcpy(Name8dot3
->Buffer
+ j
, ExtBuffer
, ExtLength
* sizeof(WCHAR
));
271 Name8dot3
->Length
= (USHORT
)(j
+ ExtLength
) * sizeof(WCHAR
);
273 DPRINT("Name8dot3: '%wZ'\n", Name8dot3
);
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
));
285 * Note: the function does not conform to the annotations.
286 * SpacesFound is not always set!
288 _IRQL_requires_max_(PASSIVE_LEVEL
)
289 _Must_inspect_result_
293 RtlIsNameLegalDOS8Dot3 (
294 _In_ PCUNICODE_STRING Name
,
295 _Inout_opt_ POEM_STRING OemName
,
296 _Out_opt_ PBOOLEAN NameContainsSpaces
)
298 static const char Illegal
[] = "*?<>|\"+=,;[]:/\\\345";
302 OEM_STRING OemString
;
303 BOOLEAN GotSpace
= FALSE
;
308 OemString
.Length
= sizeof(Buffer
);
309 OemString
.MaximumLength
= sizeof(Buffer
);
310 OemString
.Buffer
= Buffer
;
311 OemName
= &OemString
;
314 Status
= RtlUpcaseUnicodeStringToCountedOemString(OemName
, Name
, FALSE
);
315 if (!NT_SUCCESS(Status
))
318 if ((OemName
->Length
> 12) || (OemName
->Buffer
== NULL
)) return FALSE
;
320 /* a starting . is invalid, except for . and .. */
321 if (OemName
->Buffer
[0] == '.')
323 if (OemName
->Length
!= 1 && (OemName
->Length
!= 2 || OemName
->Buffer
[1] != '.')) return FALSE
;
324 if (NameContainsSpaces
) *NameContainsSpaces
= FALSE
;
328 for (i
= 0; i
< OemName
->Length
; i
++)
330 switch (OemName
->Buffer
[i
])
333 /* leading/trailing spaces not allowed */
334 if (!i
|| i
== OemName
->Length
-1 || OemName
->Buffer
[i
+1] == '.') return FALSE
;
338 if (Dot
!= -1) return FALSE
;
342 if (strchr(Illegal
, OemName
->Buffer
[i
])) return FALSE
;
346 /* check file part is shorter than 8, extension shorter than 3
347 * dot cannot be last in string
351 if (OemName
->Length
> 8) return FALSE
;
355 if (Dot
> 8 || (OemName
->Length
- Dot
> 4) || Dot
== OemName
->Length
- 1) return FALSE
;
357 if (NameContainsSpaces
) *NameContainsSpaces
= GotSpace
;