2 * PROJECT: ReactOS Win32 Base API
3 * LICENSE: GPL - See COPYING in the top level directory
4 * FILE: dll/win32/kernel32/client/path.c
5 * PURPOSE: Handles path APIs
6 * PROGRAMMERS: Alex Ionescu (alex.ionescu@reactos.org)
9 /* INCLUDES *******************************************************************/
16 /* GLOBALS ********************************************************************/
18 UNICODE_STRING BaseDllDirectory
;
19 UNICODE_STRING NoDefaultCurrentDirectoryInExePath
= RTL_CONSTANT_STRING(L
"NoDefaultCurrentDirectoryInExePath");
20 UNICODE_STRING SystemDirectory
;
21 UNICODE_STRING WindowsDirectory
;
22 UNICODE_STRING BaseDefaultPathAppend
;
23 UNICODE_STRING BaseDefaultPath
;
25 /* This is bitmask for each illegal filename character */
26 /* If someone has time, please feel free to use 0b notation */
27 DWORD IllegalMask
[4] =
29 0xFFFFFFFF, // None allowed (00 to 1F)
30 0xFC009C05, // 20, 22, 2A, 2B, 2C, 2F, 3A, 3B, 3C, 3D, 3E, 3F not allowed
31 0x38000000, // 5B, 5C, 5D not allowed
32 0x10000000 // 7C not allowed
35 /* PRIVATE FUNCTIONS **********************************************************/
38 * Why not use RtlIsNameLegalDOS8Dot3? In fact the actual algorithm body is
39 * identical (other than the Rtl can optionally check for spaces), however the
40 * Rtl will always convert to OEM, while kernel32 has two possible file modes
41 * (ANSI or OEM). Therefore we must duplicate the algorithm body to get
42 * the correct compatible results
46 IsShortName_U(IN PWCHAR Name
,
52 UNICODE_STRING UnicodeName
;
55 CHAR AnsiBuffer
[MAX_PATH
];
58 /* What do you think 8.3 means? */
59 if (Length
> 12) return FALSE
;
61 /* Sure, any emtpy name is a short name */
62 if (!Length
) return TRUE
;
64 /* This could be . or .. or something else */
68 if ((Length
== 1) || ((Length
== 2) && *(Name
+ 1) == L
'.'))
70 /* . or .., this is good */
74 /* Some other bizare dot-based name, not good */
78 /* Initialize our two strings */
79 RtlInitEmptyAnsiString(&AnsiName
, AnsiBuffer
, MAX_PATH
);
80 RtlInitEmptyUnicodeString(&UnicodeName
, Name
, Length
* sizeof(WCHAR
));
81 UnicodeName
.Length
= UnicodeName
.MaximumLength
;
83 /* Now do the conversion */
84 Status
= BasepUnicodeStringTo8BitString(&AnsiName
, &UnicodeName
, FALSE
);
85 if (!NT_SUCCESS(Status
)) return FALSE
;
87 /* Now we loop the name */
89 for (i
= 0, Dots
= Length
- 1; i
< AnsiName
.Length
; i
++, Dots
--)
91 /* Read the current byte */
92 c
= AnsiName
.Buffer
[i
];
95 if (IsDBCSLeadByte(c
))
97 /* If we're near the end of the string, we can't allow a DBCS */
98 if ((!(HasExtension
) && (i
>= 7)) || (i
== AnsiName
.Length
- 1))
103 /* Otherwise we skip over it */
107 /* Check for illegal characters */
108 if ((c
> 0x7F) || (IllegalMask
[c
/ 32] & (1 << (c
% 32))))
113 /* Check if this is perhaps an extension? */
116 /* Unless the extension is too large or there's more than one */
117 if ((HasExtension
) || (Dots
> 3)) return FALSE
;
119 /* This looks like an extension */
123 /* 8.3 length was validated, but now we must guard against 9.2 or similar */
124 if ((i
>= 8) && !(HasExtension
)) return FALSE
;
127 /* You survived the loop, this is a good short name */
133 IsLongName_U(IN PWCHAR FileName
,
136 BOOLEAN HasExtension
;
139 /* More than 8.3, any combination of dots, and NULL names are all long */
140 if (!(Length
) || (Length
> 12) || (*FileName
== L
'.')) return TRUE
;
142 /* Otherwise, initialize our scanning loop */
143 HasExtension
= FALSE
;
144 for (i
= 0, Dots
= Length
- 1; i
< Length
; i
++, Dots
--)
146 /* Check if this could be an extension */
147 if (FileName
[i
] == '.')
149 /* Unlike the short case, we WANT more than one extension, or a long one */
150 if ((HasExtension
) || (Dots
> 3))
157 /* Check if this would violate the "8" in 8.3, ie. 9.2 */
158 if ((i
>= 8) && (!HasExtension
)) return TRUE
;
161 /* The name *seems* to conform to 8.3 */
167 FindLFNorSFN_U(IN PWCHAR Path
,
177 /* Loop while there is something in the path */
180 /* Loop within the path skipping slashes */
181 while ((*Path
== L
'\\') || (*Path
== L
'/')) Path
++;
183 /* Make sure there's something after the slashes too! */
184 if (*Path
== UNICODE_NULL
) break;
186 /* Now skip past the file name until we get to the first slash */
188 while ((*p
) && ((*p
!= L
'\\') && (*p
!= L
'/'))) p
++;
190 /* Whatever is in between those two is now the file name length */
194 * Check if it is valid
195 * Note that !IsShortName != IsLongName, these two functions simply help
196 * us determine if a conversion is necessary or not.
197 * "Found" really means: "Is a conversion necessary?", hence the "!"
199 Found
= UseShort
? !IsShortName_U(Path
, Length
) : !IsLongName_U(Path
, Length
);
202 /* It is! did the caller request to know the markers? */
203 if ((First
) && (Last
))
212 /* Is there anything else following this sub-path/filename? */
213 if (*p
== UNICODE_NULL
) break;
215 /* Yes, keep going */
219 /* Return if anything was found and valid */
225 SkipPathTypeIndicator_U(IN LPWSTR Path
)
230 /* Check what kind of path this is and how many slashes to skip */
231 switch (RtlDetermineDosPathNameType_U(Path
))
233 case RtlPathTypeDriveAbsolute
:
236 case RtlPathTypeDriveRelative
:
239 case RtlPathTypeRooted
:
242 case RtlPathTypeRelative
:
245 case RtlPathTypeRootLocalDevice
:
249 case RtlPathTypeUncAbsolute
:
250 case RtlPathTypeLocalDevice
:
252 /* Keep going until we bypass the path indicators */
253 for (ReturnPath
= Path
+ 2, i
= 2; (i
> 0) && (*ReturnPath
); ReturnPath
++)
255 /* We look for 2 slashes, so keep at it until we find them */
256 if ((*ReturnPath
== L
'\\') || (*ReturnPath
== L
'/')) i
--;
265 BasepIsCurDirAllowedForPlainExeNames(VOID
)
268 UNICODE_STRING EmptyString
;
270 RtlInitEmptyUnicodeString(&EmptyString
, NULL
, 0);
271 Status
= RtlQueryEnvironmentVariable_U(NULL
,
272 &NoDefaultCurrentDirectoryInExePath
,
274 return !NT_SUCCESS(Status
) && Status
!= STATUS_BUFFER_TOO_SMALL
;
277 /* PUBLIC FUNCTIONS ***********************************************************/
284 SetDllDirectoryW(IN LPCWSTR lpPathName
)
286 UNICODE_STRING OldDirectory
, DllDirectory
;
290 if (wcschr(lpPathName
, L
';'))
292 SetLastError(ERROR_INVALID_PARAMETER
);
295 if (!RtlCreateUnicodeString(&DllDirectory
, lpPathName
))
297 SetLastError(ERROR_NOT_ENOUGH_MEMORY
);
303 RtlInitUnicodeString(&DllDirectory
, NULL
);
306 RtlEnterCriticalSection(&BaseDllDirectoryLock
);
308 OldDirectory
= BaseDllDirectory
;
309 BaseDllDirectory
= DllDirectory
;
311 RtlLeaveCriticalSection(&BaseDllDirectoryLock
);
313 RtlFreeUnicodeString(&OldDirectory
);
322 SetDllDirectoryA(IN LPCSTR lpPathName
)
324 ANSI_STRING AnsiDllDirectory
;
325 UNICODE_STRING OldDirectory
, DllDirectory
;
330 if (strchr(lpPathName
, ';'))
332 SetLastError(ERROR_INVALID_PARAMETER
);
336 Status
= RtlInitAnsiStringEx(&AnsiDllDirectory
, lpPathName
);
337 if (NT_SUCCESS(Status
))
339 Status
= Basep8BitStringToUnicodeString(&DllDirectory
,
344 if (!NT_SUCCESS(Status
))
346 BaseSetLastNTError(Status
);
352 RtlInitUnicodeString(&DllDirectory
, NULL
);
355 RtlEnterCriticalSection(&BaseDllDirectoryLock
);
357 OldDirectory
= BaseDllDirectory
;
358 BaseDllDirectory
= DllDirectory
;
360 RtlLeaveCriticalSection(&BaseDllDirectoryLock
);
362 RtlFreeUnicodeString(&OldDirectory
);
371 GetDllDirectoryW(IN DWORD nBufferLength
,
376 RtlEnterCriticalSection(&BaseDllDirectoryLock
);
378 if ((nBufferLength
* sizeof(WCHAR
)) > BaseDllDirectory
.Length
)
380 RtlCopyMemory(lpBuffer
, BaseDllDirectory
.Buffer
, BaseDllDirectory
.Length
);
381 Length
= BaseDllDirectory
.Length
/ sizeof(WCHAR
);
382 lpBuffer
[Length
] = UNICODE_NULL
;
386 Length
= (BaseDllDirectory
.Length
+ sizeof(UNICODE_NULL
)) / sizeof(WCHAR
);
387 if (lpBuffer
) *lpBuffer
= UNICODE_NULL
;
390 RtlLeaveCriticalSection(&BaseDllDirectoryLock
);
399 GetDllDirectoryA(IN DWORD nBufferLength
,
403 ANSI_STRING AnsiDllDirectory
;
406 RtlInitEmptyAnsiString(&AnsiDllDirectory
, lpBuffer
, nBufferLength
);
408 RtlEnterCriticalSection(&BaseDllDirectoryLock
);
410 Length
= BasepUnicodeStringTo8BitSize(&BaseDllDirectory
);
411 if (Length
> nBufferLength
)
413 Status
= STATUS_SUCCESS
;
414 if (lpBuffer
) *lpBuffer
= ANSI_NULL
;
419 Status
= BasepUnicodeStringTo8BitString(&AnsiDllDirectory
,
424 RtlLeaveCriticalSection(&BaseDllDirectoryLock
);
426 if (!NT_SUCCESS(Status
))
428 BaseSetLastNTError(Status
);
430 if (lpBuffer
) *lpBuffer
= ANSI_NULL
;
441 NeedCurrentDirectoryForExePathW(IN LPCWSTR ExeName
)
443 if (wcschr(ExeName
, L
'\\')) return TRUE
;
445 return BasepIsCurDirAllowedForPlainExeNames();
453 NeedCurrentDirectoryForExePathA(IN LPCSTR ExeName
)
455 if (strchr(ExeName
, '\\')) return TRUE
;
457 return BasepIsCurDirAllowedForPlainExeNames();
463 * NOTE: Many of these A functions may seem to do rather complex A<->W mapping
464 * beyond what you would usually expect. There are two main reasons:
466 * First, these APIs are subject to the ANSI/OEM File API selection status that
467 * the caller has chosen, so we must use the "8BitString" internal Base APIs.
469 * Secondly, the Wide APIs (coming from the 9x world) are coded to return the
470 * length of the paths in "ANSI" by dividing their internal Wide character count
471 * by two... this is usually correct when dealing with pure-ASCII codepages but
472 * not necessarily when dealing with MBCS pre-Unicode sets, which NT supports
473 * for CJK, for example.
477 GetFullPathNameA(IN LPCSTR lpFileName
,
478 IN DWORD nBufferLength
,
480 IN LPSTR
*lpFilePart
)
484 ULONG PathSize
, FilePartSize
;
485 ANSI_STRING AnsiString
;
486 UNICODE_STRING FileNameString
, UniString
;
487 PWCHAR LocalFilePart
;
490 /* If the caller wants filepart, use a local wide buffer since this is A */
491 FilePart
= lpFilePart
!= NULL
? &LocalFilePart
: NULL
;
493 /* Initialize for Quickie */
494 FilePartSize
= PathSize
= 0;
495 FileNameString
.Buffer
= NULL
;
497 /* First get our string in Unicode */
498 Status
= Basep8BitStringToDynamicUnicodeString(&FileNameString
, lpFileName
);
499 if (!NT_SUCCESS(Status
)) goto Quickie
;
501 /* Allocate a buffer to hold teh path name */
502 Buffer
= RtlAllocateHeap(RtlGetProcessHeap(),
504 MAX_PATH
* sizeof(WCHAR
) + sizeof(UNICODE_NULL
));
507 BaseSetLastNTError(STATUS_INSUFFICIENT_RESOURCES
);
511 /* Call into RTL to get the full Unicode path name */
512 PathSize
= RtlGetFullPathName_U(FileNameString
.Buffer
,
513 MAX_PATH
* sizeof(WCHAR
),
516 if (PathSize
<= (MAX_PATH
* sizeof(WCHAR
)))
518 /* The buffer will fit, get the real ANSI string size now */
519 Status
= RtlUnicodeToMultiByteSize(&PathSize
, Buffer
, PathSize
);
520 if (NT_SUCCESS(Status
))
522 /* Now check if the user wanted file part size as well */
523 if ((PathSize
) && (lpFilePart
) && (LocalFilePart
))
525 /* Yep, so in this case get the length of the file part too */
526 Status
= RtlUnicodeToMultiByteSize(&FilePartSize
,
528 (LocalFilePart
- Buffer
) *
530 if (!NT_SUCCESS(Status
))
532 /* We failed to do that, so fail the whole call */
533 BaseSetLastNTError(Status
);
541 /* Reset the path size since the buffer is not large enough */
545 /* Either no path, or local buffer was too small, enter failure code */
546 if (!PathSize
) goto Quickie
;
548 /* If the *caller's* buffer was too small, fail, but add in space for NULL */
549 if (PathSize
>= nBufferLength
)
555 /* So far so good, initialize a unicode string to convert back to ANSI/OEM */
556 RtlInitUnicodeString(&UniString
, Buffer
);
557 Status
= BasepUnicodeStringTo8BitString(&AnsiString
, &UniString
, TRUE
);
558 if (!NT_SUCCESS(Status
))
560 /* Final conversion failed, fail the call */
561 BaseSetLastNTError(Status
);
566 /* Conversion worked, now copy the ANSI/OEM buffer into the buffer */
567 RtlCopyMemory(lpBuffer
, AnsiString
.Buffer
, PathSize
+ 1);
568 RtlFreeAnsiString(&AnsiString
);
570 /* And finally, did the caller request file part information? */
573 /* Use the size we computed earlier and add it to the buffer */
574 *lpFilePart
= LocalFilePart
? &lpBuffer
[FilePartSize
] : 0;
579 /* Cleanup and return the path size */
580 if (FileNameString
.Buffer
) RtlFreeUnicodeString(&FileNameString
);
581 if (Buffer
) RtlFreeHeap(RtlGetProcessHeap(), 0, Buffer
);
590 GetFullPathNameW(IN LPCWSTR lpFileName
,
591 IN DWORD nBufferLength
,
593 OUT LPWSTR
*lpFilePart
)
595 /* Call Rtl to do the work */
596 return RtlGetFullPathName_U((LPWSTR
)lpFileName
,
597 nBufferLength
* sizeof(WCHAR
),
599 lpFilePart
) / sizeof(WCHAR
);
607 SearchPathA(IN LPCSTR lpPath
,
608 IN LPCSTR lpFileName
,
609 IN LPCSTR lpExtension
,
610 IN DWORD nBufferLength
,
612 OUT LPSTR
*lpFilePart
)
614 PUNICODE_STRING FileNameString
;
615 UNICODE_STRING PathString
, ExtensionString
;
617 ULONG PathSize
, FilePartSize
, AnsiLength
;
618 PWCHAR LocalFilePart
, Buffer
;
621 /* If the caller wants filepart, use a local wide buffer since this is A */
622 FilePart
= lpFilePart
!= NULL
? &LocalFilePart
: NULL
;
624 /* Initialize stuff for Quickie */
627 ExtensionString
.Buffer
= PathString
.Buffer
= NULL
;
629 /* Get the UNICODE_STRING file name */
630 FileNameString
= Basep8BitStringToStaticUnicodeString(lpFileName
);
631 if (!FileNameString
) return 0;
633 /* Did the caller specify an extension */
636 /* Yup, convert it into UNICODE_STRING */
637 Status
= Basep8BitStringToDynamicUnicodeString(&ExtensionString
,
639 if (!NT_SUCCESS(Status
)) goto Quickie
;
642 /* Did the caller specify a path */
645 /* Yup, convert it into UNICODE_STRING */
646 Status
= Basep8BitStringToDynamicUnicodeString(&PathString
, lpPath
);
647 if (!NT_SUCCESS(Status
)) goto Quickie
;
650 /* Allocate our output buffer */
651 Buffer
= RtlAllocateHeap(RtlGetProcessHeap(), 0, nBufferLength
* sizeof(WCHAR
));
654 /* It failed, bail out */
655 BaseSetLastNTError(STATUS_NO_MEMORY
);
659 /* Now run the Wide search with the input buffer lengths */
660 PathSize
= SearchPathW(PathString
.Buffer
,
661 FileNameString
->Buffer
,
662 ExtensionString
.Buffer
,
666 if (PathSize
<= nBufferLength
)
668 /* It fits, but is it empty? If so, bail out */
669 if (!PathSize
) goto Quickie
;
671 /* The length above is inexact, we need it in ANSI */
672 Status
= RtlUnicodeToMultiByteSize(&AnsiLength
, Buffer
, PathSize
* sizeof(WCHAR
));
673 if (!NT_SUCCESS(Status
))
675 /* Conversion failed, fail the call */
677 BaseSetLastNTError(Status
);
681 /* If the correct ANSI size is too big, return requird length plus a NULL */
682 if (AnsiLength
>= nBufferLength
)
684 PathSize
= AnsiLength
+ 1;
688 /* Now apply the final conversion to ANSI */
689 Status
= RtlUnicodeToMultiByteN(lpBuffer
,
693 PathSize
* sizeof(WCHAR
));
694 if (!NT_SUCCESS(Status
))
696 /* Conversion failed, fail the whole call */
698 BaseSetLastNTError(STATUS_NO_MEMORY
);
702 /* NULL-terminate and return the real ANSI length */
703 lpBuffer
[AnsiLength
] = ANSI_NULL
;
704 PathSize
= AnsiLength
;
706 /* Now check if the user wanted file part size as well */
709 /* If we didn't get a file part, clear the caller's */
716 /* Yep, so in this case get the length of the file part too */
717 Status
= RtlUnicodeToMultiByteSize(&FilePartSize
,
719 (LocalFilePart
- Buffer
) *
721 if (!NT_SUCCESS(Status
))
723 /* We failed to do that, so fail the whole call */
724 BaseSetLastNTError(Status
);
728 /* Return the file part buffer */
729 *lpFilePart
= lpBuffer
+ FilePartSize
;
735 /* Our initial buffer guess was too small, allocate a bigger one */
736 RtlFreeHeap(RtlGetProcessHeap(), 0, Buffer
);
737 Buffer
= RtlAllocateHeap(RtlGetProcessHeap(), 0, PathSize
* sizeof(WCHAR
));
740 /* Out of memory, fail everything */
741 BaseSetLastNTError(STATUS_NO_MEMORY
);
745 /* Do the search again -- it will fail, we just want the path size */
746 PathSize
= SearchPathW(PathString
.Buffer
,
747 FileNameString
->Buffer
,
748 ExtensionString
.Buffer
,
752 if (!PathSize
) goto Quickie
;
754 /* Convert it to a correct size */
755 Status
= RtlUnicodeToMultiByteSize(&PathSize
, Buffer
, PathSize
* sizeof(WCHAR
));
756 if (NT_SUCCESS(Status
))
758 /* Make space for the NULL-char */
763 /* Conversion failed for some reason, fail the call */
764 BaseSetLastNTError(Status
);
770 /* Cleanup/complete path */
771 if (Buffer
) RtlFreeHeap(RtlGetProcessHeap(), 0, Buffer
);
772 if (ExtensionString
.Buffer
) RtlFreeUnicodeString(&ExtensionString
);
773 if (PathString
.Buffer
) RtlFreeUnicodeString(&PathString
);
777 /***********************************************************************
778 * ContainsPath (Wine name: contains_pathW)
780 * Check if the file name contains a path; helper for SearchPathW.
781 * A relative path is not considered a path unless it starts with ./ or ../
785 ContainsPath(LPCWSTR name
)
787 if (RtlDetermineDosPathNameType_U(name
) != RtlPathTypeRelative
) return TRUE
;
788 if (name
[0] != '.') return FALSE
;
789 if (name
[1] == '/' || name
[1] == '\\' || name
[1] == '\0') return TRUE
;
790 return (name
[1] == '.' && (name
[2] == '/' || name
[2] == '\\'));
799 SearchPathW(LPCWSTR lpPath
,
808 if (!lpFileName
|| !lpFileName
[0])
810 SetLastError(ERROR_INVALID_PARAMETER
);
814 /* If the name contains an explicit path, ignore the path */
815 if (ContainsPath(lpFileName
))
817 /* try first without extension */
818 if (RtlDoesFileExists_U(lpFileName
))
819 return GetFullPathNameW(lpFileName
, nBufferLength
, lpBuffer
, lpFilePart
);
823 LPCWSTR p
= wcsrchr(lpFileName
, '.');
824 if (p
&& !strchr((const char *)p
, '/') && !wcschr( p
, '\\' ))
825 lpExtension
= NULL
; /* Ignore the specified extension */
828 /* Allocate a buffer for the file name and extension */
832 DWORD len
= wcslen(lpFileName
) + wcslen(lpExtension
);
834 if (!(tmp
= RtlAllocateHeap(RtlGetProcessHeap(), 0, (len
+ 1) * sizeof(WCHAR
))))
836 SetLastError(ERROR_OUTOFMEMORY
);
839 wcscpy(tmp
, lpFileName
);
840 wcscat(tmp
, lpExtension
);
841 if (RtlDoesFileExists_U(tmp
))
842 ret
= GetFullPathNameW(tmp
, nBufferLength
, lpBuffer
, lpFilePart
);
843 RtlFreeHeap(RtlGetProcessHeap(), 0, tmp
);
846 else if (lpPath
&& lpPath
[0]) /* search in the specified path */
848 ret
= RtlDosSearchPath_U(lpPath
,
851 nBufferLength
* sizeof(WCHAR
),
853 lpFilePart
) / sizeof(WCHAR
);
855 else /* search in the default path */
857 WCHAR
*DllPath
= GetDllLoadPath(NULL
);
861 ret
= RtlDosSearchPath_U(DllPath
,
864 nBufferLength
* sizeof(WCHAR
),
866 lpFilePart
) / sizeof(WCHAR
);
867 RtlFreeHeap(RtlGetProcessHeap(), 0, DllPath
);
871 SetLastError(ERROR_OUTOFMEMORY
);
876 if (!ret
) SetLastError(ERROR_FILE_NOT_FOUND
);
886 GetLongPathNameW(IN LPCWSTR lpszShortPath
,
887 IN LPWSTR lpszLongPath
,
890 PWCHAR Path
, Original
, First
, Last
, Buffer
, Src
, Dst
;
896 BOOLEAN Found
= FALSE
;
897 WIN32_FIND_DATAW FindFileData
;
899 /* Initialize so Quickie knows there's nothing to do */
900 Buffer
= Original
= NULL
;
903 /* First check if the input path was obviously NULL */
906 /* Fail the request */
907 SetLastError(ERROR_INVALID_PARAMETER
);
911 /* We will be touching removed, removable drives -- don't warn the user */
912 ErrorMode
= SetErrorMode(SEM_NOOPENFILEERRORBOX
| SEM_FAILCRITICALERRORS
);
914 /* Do a simple check to see if the path exists */
915 if (GetFileAttributesW(lpszShortPath
) == INVALID_FILE_ATTRIBUTES
)
917 /* It doesn't, so fail */
922 /* Now get a pointer to the actual path, skipping indicators */
923 Path
= SkipPathTypeIndicator_U((LPWSTR
)lpszShortPath
);
925 /* Is there any path or filename in there? */
927 (*Path
== UNICODE_NULL
) ||
928 !(FindLFNorSFN_U(Path
, &First
, &Last
, FALSE
)))
930 /* There isn't, so the long path is simply the short path */
931 ReturnLength
= wcslen(lpszShortPath
);
933 /* Is there space for it? */
934 if ((cchBuffer
> ReturnLength
) && (lpszLongPath
))
936 /* Make sure the pointers aren't already the same */
937 if (lpszLongPath
!= lpszShortPath
)
939 /* They're not -- copy the short path into the long path */
940 RtlMoveMemory(lpszLongPath
,
942 ReturnLength
* sizeof(WCHAR
) + sizeof(UNICODE_NULL
));
947 /* Otherwise, let caller know we need a bigger buffer, include NULL */
953 /* We are still in the game -- compute the current size */
954 Length
= wcslen(lpszShortPath
) + sizeof(ANSI_NULL
);
955 Original
= RtlAllocateHeap(RtlGetProcessHeap(), 0, Length
* sizeof(WCHAR
));
956 if (!Original
) goto ErrorQuickie
;
958 /* Make a copy of it */
959 RtlMoveMemory(Original
, lpszShortPath
, Length
* sizeof(WCHAR
));
961 /* Compute the new first and last markers */
962 First
= &Original
[First
- lpszShortPath
];
963 Last
= &Original
[Last
- lpszShortPath
];
965 /* Set the current destination pointer for a copy */
969 * Windows allows the paths to overlap -- we have to be careful with this and
970 * see if it's same to do so, and if not, allocate our own internal buffer
971 * that we'll return at the end.
973 * This is also why we use RtlMoveMemory everywhere. Don't use RtlCopyMemory!
975 if ((cchBuffer
) && (lpszLongPath
) &&
976 (((lpszLongPath
>= lpszShortPath
) && (lpszLongPath
< &lpszShortPath
[Length
])) ||
977 ((lpszLongPath
< lpszShortPath
) && (&lpszLongPath
[cchBuffer
] >= lpszShortPath
))))
979 Buffer
= RtlAllocateHeap(RtlGetProcessHeap(), 0, cchBuffer
* sizeof(WCHAR
));
980 if (!Buffer
) goto ErrorQuickie
;
982 /* New destination */
986 /* Prepare for the loop */
991 /* Current delta in the loop */
992 Length
= First
- Src
;
994 /* Update the return length by it */
995 ReturnLength
+= Length
;
997 /* Is there a delta? If so, is there space and buffer for it? */
998 if ((Length
) && (cchBuffer
> ReturnLength
) && (lpszLongPath
))
1000 RtlMoveMemory(Dst
, Src
, Length
* sizeof(WCHAR
));
1004 /* "Terminate" this portion of the path's substring so we can do a find */
1006 *Last
= UNICODE_NULL
;
1007 FindHandle
= FindFirstFileW(Original
, &FindFileData
);
1010 /* This portion wasn't found, so fail */
1011 if (FindHandle
== INVALID_HANDLE_VALUE
)
1017 /* Close the find handle */
1018 FindClose(FindHandle
);
1020 /* Now check the length of the long name */
1021 Length
= wcslen(FindFileData
.cFileName
);
1024 /* This is our new first marker */
1025 First
= FindFileData
.cFileName
;
1029 /* Otherwise, the name is the delta between our current markers */
1030 Length
= Last
- First
;
1033 /* Update the return length with the short name length, if any */
1034 ReturnLength
+= Length
;
1036 /* Once again check for appropriate space and buffer */
1037 if ((cchBuffer
> ReturnLength
) && (lpszLongPath
))
1039 /* And do the copy if there is */
1040 RtlMoveMemory(Dst
, First
, Length
* sizeof(WCHAR
));
1044 /* Now update the source pointer */
1046 if (*Src
== UNICODE_NULL
) break;
1048 /* Are there more names in there? */
1049 Found
= FindLFNorSFN_U(Src
, &First
, &Last
, FALSE
);
1053 /* The loop is done, is there anything left? */
1056 /* Get the length of the straggling path */
1057 Length
= wcslen(Src
);
1058 ReturnLength
+= Length
;
1060 /* Once again check for appropriate space and buffer */
1061 if ((cchBuffer
> ReturnLength
) && (lpszLongPath
))
1063 /* And do the copy if there is -- accounting for NULL here */
1064 RtlMoveMemory(Dst
, Src
, Length
* sizeof(WCHAR
) + sizeof(UNICODE_NULL
));
1066 /* What about our buffer? */
1069 /* Copy it into the caller's long path */
1070 RtlMoveMemory(lpszLongPath
,
1072 ReturnLength
* sizeof(WCHAR
) + sizeof(UNICODE_NULL
));
1077 /* Buffer is too small, let the caller know, making space for NULL */
1082 /* We're all done */
1086 /* This is the goto for memory failures */
1087 SetLastError(ERROR_NOT_ENOUGH_MEMORY
);
1090 /* General function end: free memory, restore error mode, return length */
1091 if (Original
) RtlFreeHeap(RtlGetProcessHeap(), 0, Original
);
1092 if (Buffer
) RtlFreeHeap(RtlGetProcessHeap(), 0, Buffer
);
1093 SetErrorMode(ErrorMode
);
1094 return ReturnLength
;
1102 GetLongPathNameA(IN LPCSTR lpszShortPath
,
1103 IN LPSTR lpszLongPath
,
1106 ULONG Result
, PathLength
;
1109 UNICODE_STRING LongPathUni
, ShortPathUni
;
1110 ANSI_STRING LongPathAnsi
;
1111 WCHAR LongPathBuffer
[MAX_PATH
];
1114 LongPathAnsi
.Buffer
= NULL
;
1115 ShortPathUni
.Buffer
= NULL
;
1120 SetLastError(ERROR_INVALID_PARAMETER
);
1124 Status
= Basep8BitStringToDynamicUnicodeString(&ShortPathUni
, lpszShortPath
);
1125 if (!NT_SUCCESS(Status
)) goto Quickie
;
1127 LongPath
= LongPathBuffer
;
1129 PathLength
= GetLongPathNameW(ShortPathUni
.Buffer
, LongPathBuffer
, MAX_PATH
);
1130 if (PathLength
>= MAX_PATH
)
1132 LongPath
= RtlAllocateHeap(RtlGetProcessHeap(), 0, PathLength
* sizeof(WCHAR
));
1136 SetLastError(ERROR_NOT_ENOUGH_MEMORY
);
1140 PathLength
= GetLongPathNameW(ShortPathUni
.Buffer
, LongPath
, PathLength
);
1144 if (!PathLength
) goto Quickie
;
1146 ShortPathUni
.MaximumLength
= PathLength
* sizeof(WCHAR
) + sizeof(UNICODE_NULL
);
1147 LongPathUni
.Buffer
= LongPath
;
1148 LongPathUni
.Length
= PathLength
* sizeof(WCHAR
);
1150 Status
= BasepUnicodeStringTo8BitString(&LongPathAnsi
, &LongPathUni
, TRUE
);
1151 if (!NT_SUCCESS(Status
))
1153 BaseSetLastNTError(Status
);
1157 Result
= LongPathAnsi
.Length
;
1158 if ((lpszLongPath
) && (cchBuffer
> LongPathAnsi
.Length
))
1160 RtlMoveMemory(lpszLongPath
, LongPathAnsi
.Buffer
, LongPathAnsi
.Length
);
1161 lpszLongPath
[Result
] = ANSI_NULL
;
1165 Result
= LongPathAnsi
.Length
+ sizeof(ANSI_NULL
);
1169 if (ShortPathUni
.Buffer
) RtlFreeUnicodeString(&ShortPathUni
);
1170 if (LongPathAnsi
.Buffer
) RtlFreeAnsiString(&LongPathAnsi
);
1171 if ((LongPath
) && (LongPath
!= LongPathBuffer
))
1173 RtlFreeHeap(RtlGetProcessHeap(), 0, LongPath
);
1183 GetShortPathNameA(IN LPCSTR lpszLongPath
,
1184 IN LPSTR lpszShortPath
,
1187 ULONG Result
, PathLength
;
1190 UNICODE_STRING LongPathUni
, ShortPathUni
;
1191 ANSI_STRING ShortPathAnsi
;
1192 WCHAR ShortPathBuffer
[MAX_PATH
];
1195 ShortPathAnsi
.Buffer
= NULL
;
1196 LongPathUni
.Buffer
= NULL
;
1201 SetLastError(ERROR_INVALID_PARAMETER
);
1205 Status
= Basep8BitStringToDynamicUnicodeString(&LongPathUni
, lpszLongPath
);
1206 if (!NT_SUCCESS(Status
)) goto Quickie
;
1208 ShortPath
= ShortPathBuffer
;
1210 PathLength
= GetShortPathNameW(LongPathUni
.Buffer
, ShortPathBuffer
, MAX_PATH
);
1211 if (PathLength
>= MAX_PATH
)
1213 ShortPath
= RtlAllocateHeap(RtlGetProcessHeap(), 0, PathLength
* sizeof(WCHAR
));
1217 SetLastError(ERROR_NOT_ENOUGH_MEMORY
);
1221 PathLength
= GetShortPathNameW(LongPathUni
.Buffer
, ShortPath
, PathLength
);
1225 if (!PathLength
) goto Quickie
;
1227 LongPathUni
.MaximumLength
= PathLength
* sizeof(WCHAR
) + sizeof(UNICODE_NULL
);
1228 ShortPathUni
.Buffer
= ShortPath
;
1229 ShortPathUni
.Length
= PathLength
* sizeof(WCHAR
);
1231 Status
= BasepUnicodeStringTo8BitString(&ShortPathAnsi
, &ShortPathUni
, TRUE
);
1232 if (!NT_SUCCESS(Status
))
1234 BaseSetLastNTError(Status
);
1238 Result
= ShortPathAnsi
.Length
;
1239 if ((lpszShortPath
) && (cchBuffer
> ShortPathAnsi
.Length
))
1241 RtlMoveMemory(lpszShortPath
, ShortPathAnsi
.Buffer
, ShortPathAnsi
.Length
);
1242 lpszShortPath
[Result
] = ANSI_NULL
;
1246 Result
= ShortPathAnsi
.Length
+ sizeof(ANSI_NULL
);
1250 if (LongPathUni
.Buffer
) RtlFreeUnicodeString(&LongPathUni
);
1251 if (ShortPathAnsi
.Buffer
) RtlFreeAnsiString(&ShortPathAnsi
);
1252 if ((ShortPath
) && (ShortPath
!= ShortPathBuffer
))
1254 RtlFreeHeap(RtlGetProcessHeap(), 0, ShortPath
);
1264 GetShortPathNameW(IN LPCWSTR lpszLongPath
,
1265 IN LPWSTR lpszShortPath
,
1268 PWCHAR Path
, Original
, First
, Last
, Buffer
, Src
, Dst
;
1274 BOOLEAN Found
= FALSE
;
1275 WIN32_FIND_DATAW FindFileData
;
1277 /* Initialize so Quickie knows there's nothing to do */
1278 Buffer
= Original
= NULL
;
1281 /* First check if the input path was obviously NULL */
1284 /* Fail the request */
1285 SetLastError(ERROR_INVALID_PARAMETER
);
1289 /* We will be touching removed, removable drives -- don't warn the user */
1290 ErrorMode
= SetErrorMode(SEM_NOOPENFILEERRORBOX
| SEM_FAILCRITICALERRORS
);
1292 /* Do a simple check to see if the path exists */
1293 if (GetFileAttributesW(lpszLongPath
) == INVALID_FILE_ATTRIBUTES
)
1295 /* Windows checks for an application compatibility flag to allow this */
1296 if (!(NtCurrentPeb()) || !(NtCurrentPeb()->AppCompatFlags
.LowPart
& 1))
1298 /* It doesn't, so fail */
1304 /* Now get a pointer to the actual path, skipping indicators */
1305 Path
= SkipPathTypeIndicator_U((LPWSTR
)lpszLongPath
);
1307 /* Is there any path or filename in there? */
1309 (*Path
== UNICODE_NULL
) ||
1310 !(FindLFNorSFN_U(Path
, &First
, &Last
, TRUE
)))
1312 /* There isn't, so the long path is simply the short path */
1313 ReturnLength
= wcslen(lpszLongPath
);
1315 /* Is there space for it? */
1316 if ((cchBuffer
> ReturnLength
) && (lpszShortPath
))
1318 /* Make sure the pointers aren't already the same */
1319 if (lpszLongPath
!= lpszShortPath
)
1321 /* They're not -- copy the short path into the long path */
1322 RtlMoveMemory(lpszShortPath
,
1324 ReturnLength
* sizeof(WCHAR
) + sizeof(UNICODE_NULL
));
1329 /* Otherwise, let caller know we need a bigger buffer, include NULL */
1335 /* We are still in the game -- compute the current size */
1336 Length
= wcslen(lpszLongPath
) + sizeof(ANSI_NULL
);
1337 Original
= RtlAllocateHeap(RtlGetProcessHeap(), 0, Length
* sizeof(WCHAR
));
1338 if (!Original
) goto ErrorQuickie
;
1340 /* Make a copy of it */
1341 wcsncpy(Original
, lpszLongPath
, Length
);
1343 /* Compute the new first and last markers */
1344 First
= &Original
[First
- lpszLongPath
];
1345 Last
= &Original
[Last
- lpszLongPath
];
1347 /* Set the current destination pointer for a copy */
1348 Dst
= lpszShortPath
;
1351 * Windows allows the paths to overlap -- we have to be careful with this and
1352 * see if it's same to do so, and if not, allocate our own internal buffer
1353 * that we'll return at the end.
1355 * This is also why we use RtlMoveMemory everywhere. Don't use RtlCopyMemory!
1357 if ((cchBuffer
) && (lpszShortPath
) &&
1358 (((lpszShortPath
>= lpszLongPath
) && (lpszShortPath
< &lpszLongPath
[Length
])) ||
1359 ((lpszShortPath
< lpszLongPath
) && (&lpszShortPath
[cchBuffer
] >= lpszLongPath
))))
1361 Buffer
= RtlAllocateHeap(RtlGetProcessHeap(), 0, cchBuffer
* sizeof(WCHAR
));
1362 if (!Buffer
) goto ErrorQuickie
;
1364 /* New destination */
1368 /* Prepare for the loop */
1373 /* Current delta in the loop */
1374 Length
= First
- Src
;
1376 /* Update the return length by it */
1377 ReturnLength
+= Length
;
1379 /* Is there a delta? If so, is there space and buffer for it? */
1380 if ((Length
) && (cchBuffer
> ReturnLength
) && (lpszShortPath
))
1382 RtlMoveMemory(Dst
, Src
, Length
* sizeof(WCHAR
));
1386 /* "Terminate" this portion of the path's substring so we can do a find */
1388 *Last
= UNICODE_NULL
;
1389 FindHandle
= FindFirstFileW(Original
, &FindFileData
);
1392 /* This portion wasn't found, so fail */
1393 if (FindHandle
== INVALID_HANDLE_VALUE
)
1399 /* Close the find handle */
1400 FindClose(FindHandle
);
1402 /* Now check the length of the short name */
1403 Length
= wcslen(FindFileData
.cAlternateFileName
);
1406 /* This is our new first marker */
1407 First
= FindFileData
.cAlternateFileName
;
1411 /* Otherwise, the name is the delta between our current markers */
1412 Length
= Last
- First
;
1415 /* Update the return length with the short name length, if any */
1416 ReturnLength
+= Length
;
1418 /* Once again check for appropriate space and buffer */
1419 if ((cchBuffer
> ReturnLength
) && (lpszShortPath
))
1421 /* And do the copy if there is */
1422 RtlMoveMemory(Dst
, First
, Length
* sizeof(WCHAR
));
1426 /* Now update the source pointer */
1428 if (*Src
== UNICODE_NULL
) break;
1430 /* Are there more names in there? */
1431 Found
= FindLFNorSFN_U(Src
, &First
, &Last
, TRUE
);
1435 /* The loop is done, is there anything left? */
1438 /* Get the length of the straggling path */
1439 Length
= wcslen(Src
);
1440 ReturnLength
+= Length
;
1442 /* Once again check for appropriate space and buffer */
1443 if ((cchBuffer
> ReturnLength
) && (lpszShortPath
))
1445 /* And do the copy if there is -- accounting for NULL here */
1446 RtlMoveMemory(Dst
, Src
, Length
* sizeof(WCHAR
) + sizeof(UNICODE_NULL
));
1448 /* What about our buffer? */
1451 /* Copy it into the caller's long path */
1452 RtlMoveMemory(lpszShortPath
,
1454 ReturnLength
* sizeof(WCHAR
) + sizeof(UNICODE_NULL
));
1459 /* Buffer is too small, let the caller know, making space for NULL */
1464 /* We're all done */
1468 /* This is the goto for memory failures */
1469 SetLastError(ERROR_NOT_ENOUGH_MEMORY
);
1472 /* General function end: free memory, restore error mode, return length */
1473 if (Original
) RtlFreeHeap(RtlGetProcessHeap(), 0, Original
);
1474 if (Buffer
) RtlFreeHeap(RtlGetProcessHeap(), 0, Buffer
);
1475 SetErrorMode(ErrorMode
);
1476 return ReturnLength
;
1484 GetCurrentDirectoryA(IN DWORD nBufferLength
,
1487 WCHAR BufferW
[MAX_PATH
];
1490 ret
= GetCurrentDirectoryW(MAX_PATH
, BufferW
);
1495 SetLastError(ERROR_FILENAME_EXCED_RANGE
);
1499 return FilenameW2A_FitOrFail(lpBuffer
, nBufferLength
, BufferW
, ret
+1);
1507 GetCurrentDirectoryW(IN DWORD nBufferLength
,
1512 Length
= RtlGetCurrentDirectory_U (nBufferLength
* sizeof(WCHAR
), lpBuffer
);
1513 return (Length
/ sizeof (WCHAR
));
1521 SetCurrentDirectoryA(IN LPCSTR lpPathName
)
1525 DPRINT("setcurrdir: %s\n",lpPathName
);
1527 if (!(PathNameW
= FilenameA2W(lpPathName
, FALSE
))) return FALSE
;
1529 return SetCurrentDirectoryW(PathNameW
);
1537 SetCurrentDirectoryW(IN LPCWSTR lpPathName
)
1539 UNICODE_STRING UnicodeString
;
1542 RtlInitUnicodeString(&UnicodeString
, lpPathName
);
1544 Status
= RtlSetCurrentDirectory_U(&UnicodeString
);
1545 if (!NT_SUCCESS(Status
))
1547 BaseSetLastNTError (Status
);
1557 * NOTE: Windows returns a dos/short (8.3) path
1561 GetTempPathA(IN DWORD nBufferLength
,
1564 WCHAR BufferW
[MAX_PATH
];
1567 ret
= GetTempPathW(MAX_PATH
, BufferW
);
1573 SetLastError(ERROR_FILENAME_EXCED_RANGE
);
1577 return FilenameW2A_FitOrFail(lpBuffer
, nBufferLength
, BufferW
, ret
+1);
1587 GetTempPathW(IN DWORD count
,
1590 static const WCHAR tmp
[] = { 'T', 'M', 'P', 0 };
1591 static const WCHAR temp
[] = { 'T', 'E', 'M', 'P', 0 };
1592 static const WCHAR userprofile
[] = { 'U','S','E','R','P','R','O','F','I','L','E',0 };
1593 WCHAR tmp_path
[MAX_PATH
];
1596 DPRINT("%u,%p\n", count
, path
);
1598 if (!(ret
= GetEnvironmentVariableW( tmp
, tmp_path
, MAX_PATH
)) &&
1599 !(ret
= GetEnvironmentVariableW( temp
, tmp_path
, MAX_PATH
)) &&
1600 !(ret
= GetEnvironmentVariableW( userprofile
, tmp_path
, MAX_PATH
)) &&
1601 !(ret
= GetWindowsDirectoryW( tmp_path
, MAX_PATH
)))
1606 SetLastError(ERROR_FILENAME_EXCED_RANGE
);
1610 ret
= GetFullPathNameW(tmp_path
, MAX_PATH
, tmp_path
, NULL
);
1613 if (ret
> MAX_PATH
- 2)
1615 SetLastError(ERROR_FILENAME_EXCED_RANGE
);
1619 if (tmp_path
[ret
-1] != '\\')
1621 tmp_path
[ret
++] = '\\';
1622 tmp_path
[ret
] = '\0';
1625 ret
++; /* add space for terminating 0 */
1629 lstrcpynW(path
, tmp_path
, count
);
1631 ret
--; /* return length without 0 */
1633 path
[0] = 0; /* avoid returning ambiguous "X:" */
1636 DPRINT("GetTempPathW returning %u, %S\n", ret
, path
);
1645 GetSystemDirectoryA(IN LPSTR lpBuffer
,
1648 return FilenameU2A_FitOrFail(lpBuffer
, uSize
, &SystemDirectory
);
1656 GetSystemDirectoryW(IN LPWSTR lpBuffer
,
1661 Length
= SystemDirectory
.Length
/ sizeof (WCHAR
);
1663 if (lpBuffer
== NULL
) return Length
+ 1;
1667 memmove(lpBuffer
, SystemDirectory
.Buffer
, SystemDirectory
.Length
);
1668 lpBuffer
[Length
] = 0;
1670 return Length
; //good: ret chars excl. nullchar
1673 return Length
+1; //bad: ret space needed incl. nullchar
1681 GetWindowsDirectoryA(IN LPSTR lpBuffer
,
1684 return FilenameU2A_FitOrFail(lpBuffer
, uSize
, &WindowsDirectory
);
1692 GetWindowsDirectoryW(IN LPWSTR lpBuffer
,
1697 Length
= WindowsDirectory
.Length
/ sizeof (WCHAR
);
1699 if (lpBuffer
== NULL
) return Length
+ 1;
1703 memmove(lpBuffer
, WindowsDirectory
.Buffer
, WindowsDirectory
.Length
);
1704 lpBuffer
[Length
] = 0;
1706 return Length
; //good: ret chars excl. nullchar
1709 return Length
+1; //bad: ret space needed incl. nullchar
1717 GetSystemWindowsDirectoryA(IN LPSTR lpBuffer
,
1720 return GetWindowsDirectoryA(lpBuffer
, uSize
);
1728 GetSystemWindowsDirectoryW(IN LPWSTR lpBuffer
,
1731 return GetWindowsDirectoryW(lpBuffer
, uSize
);
1739 GetSystemWow64DirectoryW(IN LPWSTR lpBuffer
,
1746 SetLastError(ERROR_CALL_NOT_IMPLEMENTED
);
1756 GetSystemWow64DirectoryA(IN LPSTR lpBuffer
,
1763 SetLastError(ERROR_CALL_NOT_IMPLEMENTED
);