[KERNEL32]
[reactos.git] / reactos / dll / win32 / kernel32 / client / path.c
1 /*
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)
7 */
8
9 /* INCLUDES *******************************************************************/
10
11 #include <k32.h>
12
13 #define NDEBUG
14 #include <debug.h>
15
16 /* GLOBALS ********************************************************************/
17
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;
24
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] =
28 {
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
33 };
34
35 /* PRIVATE FUNCTIONS **********************************************************/
36
37 /*
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
43 */
44 BOOL
45 WINAPI
46 IsShortName_U(IN PWCHAR Name,
47 IN ULONG Length)
48 {
49 BOOLEAN HasExtension;
50 WCHAR c;
51 NTSTATUS Status;
52 UNICODE_STRING UnicodeName;
53 ANSI_STRING AnsiName;
54 ULONG i, Dots;
55 CHAR AnsiBuffer[MAX_PATH];
56 ASSERT(Name);
57
58 /* What do you think 8.3 means? */
59 if (Length > 12) return FALSE;
60
61 /* Sure, any emtpy name is a short name */
62 if (!Length) return TRUE;
63
64 /* This could be . or .. or something else */
65 if (*Name == L'.')
66 {
67 /* Which one is it */
68 if ((Length == 1) || ((Length == 2) && *(Name + 1) == L'.'))
69 {
70 /* . or .., this is good */
71 return TRUE;
72 }
73
74 /* Some other bizare dot-based name, not good */
75 return FALSE;
76 }
77
78 /* Initialize our two strings */
79 RtlInitEmptyAnsiString(&AnsiName, AnsiBuffer, MAX_PATH);
80 RtlInitEmptyUnicodeString(&UnicodeName, Name, Length * sizeof(WCHAR));
81 UnicodeName.Length = UnicodeName.MaximumLength;
82
83 /* Now do the conversion */
84 Status = BasepUnicodeStringTo8BitString(&AnsiName, &UnicodeName, FALSE);
85 if (!NT_SUCCESS(Status)) return FALSE;
86
87 /* Now we loop the name */
88 HasExtension = FALSE;
89 for (i = 0, Dots = Length - 1; i < AnsiName.Length; i++, Dots--)
90 {
91 /* Read the current byte */
92 c = AnsiName.Buffer[i];
93
94 /* Is it DBCS? */
95 if (IsDBCSLeadByte(c))
96 {
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))
99 {
100 return FALSE;
101 }
102
103 /* Otherwise we skip over it */
104 continue;
105 }
106
107 /* Check for illegal characters */
108 if ((c > 0x7F) || (IllegalMask[c / 32] & (1 << (c % 32))))
109 {
110 return FALSE;
111 }
112
113 /* Check if this is perhaps an extension? */
114 if (c == '.')
115 {
116 /* Unless the extension is too large or there's more than one */
117 if ((HasExtension) || (Dots > 3)) return FALSE;
118
119 /* This looks like an extension */
120 HasExtension = TRUE;
121 }
122
123 /* 8.3 length was validated, but now we must guard against 9.2 or similar */
124 if ((i >= 8) && !(HasExtension)) return FALSE;
125 }
126
127 /* You survived the loop, this is a good short name */
128 return TRUE;
129 }
130
131 BOOL
132 WINAPI
133 IsLongName_U(IN PWCHAR FileName,
134 IN ULONG Length)
135 {
136 BOOLEAN HasExtension;
137 ULONG i, Dots;
138
139 /* More than 8.3, any combination of dots, and NULL names are all long */
140 if (!(Length) || (Length > 12) || (*FileName == L'.')) return TRUE;
141
142 /* Otherwise, initialize our scanning loop */
143 HasExtension = FALSE;
144 for (i = 0, Dots = Length - 1; i < Length; i++, Dots--)
145 {
146 /* Check if this could be an extension */
147 if (FileName[i] == L'.')
148 {
149 /* Unlike the short case, we WANT more than one extension, or a long one */
150 if ((HasExtension) || (Dots > 3))
151 {
152 return TRUE;
153 }
154 HasExtension = TRUE;
155 }
156
157 /* Check if this would violate the "8" in 8.3, ie. 9.2 */
158 if ((i >= 8) && (!HasExtension)) return TRUE;
159 }
160
161 /* The name *seems* to conform to 8.3 */
162 return FALSE;
163 }
164
165 BOOL
166 WINAPI
167 FindLFNorSFN_U(IN PWCHAR Path,
168 OUT PWCHAR *First,
169 OUT PWCHAR *Last,
170 IN BOOL UseShort)
171 {
172 PWCHAR p;
173 ULONG Length;
174 BOOL Found = 0;
175 ASSERT(Path);
176
177 /* Loop while there is something in the path */
178 while (TRUE)
179 {
180 /* Loop within the path skipping slashes */
181 while ((*Path == L'\\') || (*Path == L'/')) Path++;
182
183 /* Make sure there's something after the slashes too! */
184 if (*Path == UNICODE_NULL) break;
185
186 /* Now skip past the file name until we get to the first slash */
187 p = Path + 1;
188 while ((*p) && ((*p != L'\\') && (*p != L'/'))) p++;
189
190 /* Whatever is in between those two is now the file name length */
191 Length = p - Path;
192
193 /*
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 "!"
198 */
199 Found = UseShort ? !IsShortName_U(Path, Length) : !IsLongName_U(Path, Length);
200 if (Found)
201 {
202 /* It is! did the caller request to know the markers? */
203 if ((First) && (Last))
204 {
205 /* Return them */
206 *First = Path;
207 *Last = p;
208 }
209 break;
210 }
211
212 /* Is there anything else following this sub-path/filename? */
213 if (*p == UNICODE_NULL) break;
214
215 /* Yes, keep going */
216 Path = p + 1;
217 }
218
219 /* Return if anything was found and valid */
220 return Found;
221 }
222
223 PWCHAR
224 WINAPI
225 SkipPathTypeIndicator_U(IN LPWSTR Path)
226 {
227 PWCHAR ReturnPath;
228 ULONG i;
229
230 /* Check what kind of path this is and how many slashes to skip */
231 switch (RtlDetermineDosPathNameType_U(Path))
232 {
233 case RtlPathTypeDriveAbsolute:
234 return Path + 3;
235
236 case RtlPathTypeDriveRelative:
237 return Path + 2;
238
239 case RtlPathTypeRooted:
240 return Path + 1;
241
242 case RtlPathTypeRelative:
243 return Path;
244
245 case RtlPathTypeRootLocalDevice:
246 default:
247 return NULL;
248
249 case RtlPathTypeUncAbsolute:
250 case RtlPathTypeLocalDevice:
251
252 /* Keep going until we bypass the path indicators */
253 for (ReturnPath = Path + 2, i = 2; (i > 0) && (*ReturnPath); ReturnPath++)
254 {
255 /* We look for 2 slashes, so keep at it until we find them */
256 if ((*ReturnPath == L'\\') || (*ReturnPath == L'/')) i--;
257 }
258
259 return ReturnPath;
260 }
261 }
262
263 BOOL
264 WINAPI
265 BasepIsCurDirAllowedForPlainExeNames(VOID)
266 {
267 NTSTATUS Status;
268 UNICODE_STRING EmptyString;
269
270 RtlInitEmptyUnicodeString(&EmptyString, NULL, 0);
271 Status = RtlQueryEnvironmentVariable_U(NULL,
272 &NoDefaultCurrentDirectoryInExePath,
273 &EmptyString);
274 return !NT_SUCCESS(Status) && Status != STATUS_BUFFER_TOO_SMALL;
275 }
276
277 /* PUBLIC FUNCTIONS ***********************************************************/
278
279 /*
280 * @implemented
281 */
282 BOOL
283 WINAPI
284 SetDllDirectoryW(IN LPCWSTR lpPathName)
285 {
286 UNICODE_STRING OldDirectory, DllDirectory;
287
288 if (lpPathName)
289 {
290 if (wcschr(lpPathName, L';'))
291 {
292 SetLastError(ERROR_INVALID_PARAMETER);
293 return FALSE;
294 }
295 if (!RtlCreateUnicodeString(&DllDirectory, lpPathName))
296 {
297 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
298 return FALSE;
299 }
300 }
301 else
302 {
303 RtlInitUnicodeString(&DllDirectory, NULL);
304 }
305
306 RtlEnterCriticalSection(&BaseDllDirectoryLock);
307
308 OldDirectory = BaseDllDirectory;
309 BaseDllDirectory = DllDirectory;
310
311 RtlLeaveCriticalSection(&BaseDllDirectoryLock);
312
313 RtlFreeUnicodeString(&OldDirectory);
314 return TRUE;
315 }
316
317 /*
318 * @implemented
319 */
320 BOOL
321 WINAPI
322 SetDllDirectoryA(IN LPCSTR lpPathName)
323 {
324 ANSI_STRING AnsiDllDirectory;
325 UNICODE_STRING OldDirectory, DllDirectory;
326 NTSTATUS Status;
327
328 if (lpPathName)
329 {
330 if (strchr(lpPathName, ';'))
331 {
332 SetLastError(ERROR_INVALID_PARAMETER);
333 return FALSE;
334 }
335
336 Status = RtlInitAnsiStringEx(&AnsiDllDirectory, lpPathName);
337 if (NT_SUCCESS(Status))
338 {
339 Status = Basep8BitStringToUnicodeString(&DllDirectory,
340 &AnsiDllDirectory,
341 TRUE);
342 }
343
344 if (!NT_SUCCESS(Status))
345 {
346 BaseSetLastNTError(Status);
347 return FALSE;
348 }
349 }
350 else
351 {
352 RtlInitUnicodeString(&DllDirectory, NULL);
353 }
354
355 RtlEnterCriticalSection(&BaseDllDirectoryLock);
356
357 OldDirectory = BaseDllDirectory;
358 BaseDllDirectory = DllDirectory;
359
360 RtlLeaveCriticalSection(&BaseDllDirectoryLock);
361
362 RtlFreeUnicodeString(&OldDirectory);
363 return TRUE;
364 }
365
366 /*
367 * @implemented
368 */
369 DWORD
370 WINAPI
371 GetDllDirectoryW(IN DWORD nBufferLength,
372 OUT LPWSTR lpBuffer)
373 {
374 ULONG Length;
375
376 RtlEnterCriticalSection(&BaseDllDirectoryLock);
377
378 if ((nBufferLength * sizeof(WCHAR)) > BaseDllDirectory.Length)
379 {
380 RtlCopyMemory(lpBuffer, BaseDllDirectory.Buffer, BaseDllDirectory.Length);
381 Length = BaseDllDirectory.Length / sizeof(WCHAR);
382 lpBuffer[Length] = UNICODE_NULL;
383 }
384 else
385 {
386 Length = (BaseDllDirectory.Length + sizeof(UNICODE_NULL)) / sizeof(WCHAR);
387 if (lpBuffer) *lpBuffer = UNICODE_NULL;
388 }
389
390 RtlLeaveCriticalSection(&BaseDllDirectoryLock);
391 return Length;
392 }
393
394 /*
395 * @implemented
396 */
397 DWORD
398 WINAPI
399 GetDllDirectoryA(IN DWORD nBufferLength,
400 OUT LPSTR lpBuffer)
401 {
402 NTSTATUS Status;
403 ANSI_STRING AnsiDllDirectory;
404 ULONG Length;
405
406 RtlInitEmptyAnsiString(&AnsiDllDirectory, lpBuffer, nBufferLength);
407
408 RtlEnterCriticalSection(&BaseDllDirectoryLock);
409
410 Length = BasepUnicodeStringTo8BitSize(&BaseDllDirectory);
411 if (Length > nBufferLength)
412 {
413 Status = STATUS_SUCCESS;
414 if (lpBuffer) *lpBuffer = ANSI_NULL;
415 }
416 else
417 {
418 --Length;
419 Status = BasepUnicodeStringTo8BitString(&AnsiDllDirectory,
420 &BaseDllDirectory,
421 FALSE);
422 }
423
424 RtlLeaveCriticalSection(&BaseDllDirectoryLock);
425
426 if (!NT_SUCCESS(Status))
427 {
428 BaseSetLastNTError(Status);
429 Length = 0;
430 if (lpBuffer) *lpBuffer = ANSI_NULL;
431 }
432
433 return Length;
434 }
435
436 /*
437 * @implemented
438 */
439 BOOL
440 WINAPI
441 NeedCurrentDirectoryForExePathW(IN LPCWSTR ExeName)
442 {
443 if (wcschr(ExeName, L'\\')) return TRUE;
444
445 return BasepIsCurDirAllowedForPlainExeNames();
446 }
447
448 /*
449 * @implemented
450 */
451 BOOL
452 WINAPI
453 NeedCurrentDirectoryForExePathA(IN LPCSTR ExeName)
454 {
455 if (strchr(ExeName, '\\')) return TRUE;
456
457 return BasepIsCurDirAllowedForPlainExeNames();
458 }
459
460 /*
461 * @implemented
462 *
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:
465 *
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.
468 *
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.
474 */
475 DWORD
476 WINAPI
477 GetFullPathNameA(IN LPCSTR lpFileName,
478 IN DWORD nBufferLength,
479 IN LPSTR lpBuffer,
480 IN LPSTR *lpFilePart)
481 {
482 NTSTATUS Status;
483 PWCHAR Buffer;
484 ULONG PathSize, FilePartSize;
485 ANSI_STRING AnsiString;
486 UNICODE_STRING FileNameString, UniString;
487 PWCHAR LocalFilePart;
488 PWCHAR* FilePart;
489
490 /* If the caller wants filepart, use a local wide buffer since this is A */
491 FilePart = lpFilePart != NULL ? &LocalFilePart : NULL;
492
493 /* Initialize for Quickie */
494 FilePartSize = PathSize = 0;
495 FileNameString.Buffer = NULL;
496
497 /* First get our string in Unicode */
498 Status = Basep8BitStringToDynamicUnicodeString(&FileNameString, lpFileName);
499 if (!NT_SUCCESS(Status)) goto Quickie;
500
501 /* Allocate a buffer to hold teh path name */
502 Buffer = RtlAllocateHeap(RtlGetProcessHeap(),
503 0,
504 MAX_PATH * sizeof(WCHAR) + sizeof(UNICODE_NULL));
505 if (!Buffer)
506 {
507 BaseSetLastNTError(STATUS_INSUFFICIENT_RESOURCES);
508 goto Quickie;
509 }
510
511 /* Call into RTL to get the full Unicode path name */
512 PathSize = RtlGetFullPathName_U(FileNameString.Buffer,
513 MAX_PATH * sizeof(WCHAR),
514 Buffer,
515 FilePart);
516 if (PathSize <= (MAX_PATH * sizeof(WCHAR)))
517 {
518 /* The buffer will fit, get the real ANSI string size now */
519 Status = RtlUnicodeToMultiByteSize(&PathSize, Buffer, PathSize);
520 if (NT_SUCCESS(Status))
521 {
522 /* Now check if the user wanted file part size as well */
523 if ((PathSize) && (lpFilePart) && (LocalFilePart))
524 {
525 /* Yep, so in this case get the length of the file part too */
526 Status = RtlUnicodeToMultiByteSize(&FilePartSize,
527 Buffer,
528 (LocalFilePart - Buffer) *
529 sizeof(WCHAR));
530 if (!NT_SUCCESS(Status))
531 {
532 /* We failed to do that, so fail the whole call */
533 BaseSetLastNTError(Status);
534 PathSize = 0;
535 }
536 }
537 }
538 }
539 else
540 {
541 /* Reset the path size since the buffer is not large enough */
542 PathSize = 0;
543 }
544
545 /* Either no path, or local buffer was too small, enter failure code */
546 if (!PathSize) goto Quickie;
547
548 /* If the *caller's* buffer was too small, fail, but add in space for NULL */
549 if (PathSize >= nBufferLength)
550 {
551 PathSize++;
552 goto Quickie;
553 }
554
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))
559 {
560 /* Final conversion failed, fail the call */
561 BaseSetLastNTError(Status);
562 PathSize = 0;
563 }
564 else
565 {
566 /* Conversion worked, now copy the ANSI/OEM buffer into the buffer */
567 RtlCopyMemory(lpBuffer, AnsiString.Buffer, PathSize + 1);
568 RtlFreeAnsiString(&AnsiString);
569
570 /* And finally, did the caller request file part information? */
571 if (lpFilePart)
572 {
573 /* Use the size we computed earlier and add it to the buffer */
574 *lpFilePart = LocalFilePart ? &lpBuffer[FilePartSize] : 0;
575 }
576 }
577
578 Quickie:
579 /* Cleanup and return the path size */
580 if (FileNameString.Buffer) RtlFreeUnicodeString(&FileNameString);
581 if (Buffer) RtlFreeHeap(RtlGetProcessHeap(), 0, Buffer);
582 return PathSize;
583 }
584
585 /*
586 * @implemented
587 */
588 DWORD
589 WINAPI
590 GetFullPathNameW(IN LPCWSTR lpFileName,
591 IN DWORD nBufferLength,
592 IN LPWSTR lpBuffer,
593 OUT LPWSTR *lpFilePart)
594 {
595 /* Call Rtl to do the work */
596 return RtlGetFullPathName_U((LPWSTR)lpFileName,
597 nBufferLength * sizeof(WCHAR),
598 lpBuffer,
599 lpFilePart) / sizeof(WCHAR);
600 }
601
602 /*
603 * @implemented
604 */
605 DWORD
606 WINAPI
607 SearchPathA(IN LPCSTR lpPath,
608 IN LPCSTR lpFileName,
609 IN LPCSTR lpExtension,
610 IN DWORD nBufferLength,
611 IN LPSTR lpBuffer,
612 OUT LPSTR *lpFilePart)
613 {
614 PUNICODE_STRING FileNameString;
615 UNICODE_STRING PathString, ExtensionString;
616 NTSTATUS Status;
617 ULONG PathSize, FilePartSize, AnsiLength;
618 PWCHAR LocalFilePart, Buffer;
619 PWCHAR* FilePart;
620
621 /* If the caller wants filepart, use a local wide buffer since this is A */
622 FilePart = lpFilePart != NULL ? &LocalFilePart : NULL;
623
624 /* Initialize stuff for Quickie */
625 PathSize = 0;
626 Buffer = NULL;
627 ExtensionString.Buffer = PathString.Buffer = NULL;
628
629 /* Get the UNICODE_STRING file name */
630 FileNameString = Basep8BitStringToStaticUnicodeString(lpFileName);
631 if (!FileNameString) return 0;
632
633 /* Did the caller specify an extension */
634 if (lpExtension)
635 {
636 /* Yup, convert it into UNICODE_STRING */
637 Status = Basep8BitStringToDynamicUnicodeString(&ExtensionString,
638 lpExtension);
639 if (!NT_SUCCESS(Status)) goto Quickie;
640 }
641
642 /* Did the caller specify a path */
643 if (lpPath)
644 {
645 /* Yup, convert it into UNICODE_STRING */
646 Status = Basep8BitStringToDynamicUnicodeString(&PathString, lpPath);
647 if (!NT_SUCCESS(Status)) goto Quickie;
648 }
649
650 /* Allocate our output buffer */
651 Buffer = RtlAllocateHeap(RtlGetProcessHeap(), 0, nBufferLength * sizeof(WCHAR));
652 if (!Buffer)
653 {
654 /* It failed, bail out */
655 BaseSetLastNTError(STATUS_NO_MEMORY);
656 goto Quickie;
657 }
658
659 /* Now run the Wide search with the input buffer lengths */
660 PathSize = SearchPathW(PathString.Buffer,
661 FileNameString->Buffer,
662 ExtensionString.Buffer,
663 nBufferLength,
664 Buffer,
665 FilePart);
666 if (PathSize <= nBufferLength)
667 {
668 /* It fits, but is it empty? If so, bail out */
669 if (!PathSize) goto Quickie;
670
671 /* The length above is inexact, we need it in ANSI */
672 Status = RtlUnicodeToMultiByteSize(&AnsiLength, Buffer, PathSize * sizeof(WCHAR));
673 if (!NT_SUCCESS(Status))
674 {
675 /* Conversion failed, fail the call */
676 PathSize = 0;
677 BaseSetLastNTError(Status);
678 goto Quickie;
679 }
680
681 /* If the correct ANSI size is too big, return requird length plus a NULL */
682 if (AnsiLength >= nBufferLength)
683 {
684 PathSize = AnsiLength + 1;
685 goto Quickie;
686 }
687
688 /* Now apply the final conversion to ANSI */
689 Status = RtlUnicodeToMultiByteN(lpBuffer,
690 nBufferLength - 1,
691 &AnsiLength,
692 Buffer,
693 PathSize * sizeof(WCHAR));
694 if (!NT_SUCCESS(Status))
695 {
696 /* Conversion failed, fail the whole call */
697 PathSize = 0;
698 BaseSetLastNTError(STATUS_NO_MEMORY);
699 goto Quickie;
700 }
701
702 /* NULL-terminate and return the real ANSI length */
703 lpBuffer[AnsiLength] = ANSI_NULL;
704 PathSize = AnsiLength;
705
706 /* Now check if the user wanted file part size as well */
707 if (lpFilePart)
708 {
709 /* If we didn't get a file part, clear the caller's */
710 if (!LocalFilePart)
711 {
712 *lpFilePart = NULL;
713 }
714 else
715 {
716 /* Yep, so in this case get the length of the file part too */
717 Status = RtlUnicodeToMultiByteSize(&FilePartSize,
718 Buffer,
719 (LocalFilePart - Buffer) *
720 sizeof(WCHAR));
721 if (!NT_SUCCESS(Status))
722 {
723 /* We failed to do that, so fail the whole call */
724 BaseSetLastNTError(Status);
725 PathSize = 0;
726 }
727
728 /* Return the file part buffer */
729 *lpFilePart = lpBuffer + FilePartSize;
730 }
731 }
732 }
733 else
734 {
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));
738 if (!Buffer)
739 {
740 /* Out of memory, fail everything */
741 BaseSetLastNTError(STATUS_NO_MEMORY);
742 goto Quickie;
743 }
744
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,
749 PathSize,
750 Buffer,
751 FilePart);
752 if (!PathSize) goto Quickie;
753
754 /* Convert it to a correct size */
755 Status = RtlUnicodeToMultiByteSize(&PathSize, Buffer, PathSize * sizeof(WCHAR));
756 if (NT_SUCCESS(Status))
757 {
758 /* Make space for the NULL-char */
759 PathSize++;
760 }
761 else
762 {
763 /* Conversion failed for some reason, fail the call */
764 BaseSetLastNTError(Status);
765 PathSize = 0;
766 }
767 }
768
769 Quickie:
770 /* Cleanup/complete path */
771 if (Buffer) RtlFreeHeap(RtlGetProcessHeap(), 0, Buffer);
772 if (ExtensionString.Buffer) RtlFreeUnicodeString(&ExtensionString);
773 if (PathString.Buffer) RtlFreeUnicodeString(&PathString);
774 return PathSize;
775 }
776
777 /***********************************************************************
778 * ContainsPath (Wine name: contains_pathW)
779 *
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 ../
782 */
783 static
784 BOOL
785 ContainsPath(LPCWSTR name)
786 {
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] == '\\'));
791 }
792
793
794 /*
795 * @implemented
796 */
797 DWORD
798 WINAPI
799 SearchPathW(LPCWSTR lpPath,
800 LPCWSTR lpFileName,
801 LPCWSTR lpExtension,
802 DWORD nBufferLength,
803 LPWSTR lpBuffer,
804 LPWSTR *lpFilePart)
805 {
806 DWORD ret = 0;
807
808 if (!lpFileName || !lpFileName[0])
809 {
810 SetLastError(ERROR_INVALID_PARAMETER);
811 return 0;
812 }
813
814 /* If the name contains an explicit path, ignore the path */
815 if (ContainsPath(lpFileName))
816 {
817 /* try first without extension */
818 if (RtlDoesFileExists_U(lpFileName))
819 return GetFullPathNameW(lpFileName, nBufferLength, lpBuffer, lpFilePart);
820
821 if (lpExtension)
822 {
823 LPCWSTR p = wcsrchr(lpFileName, '.');
824 if (p && !strchr((const char *)p, '/') && !wcschr( p, '\\' ))
825 lpExtension = NULL; /* Ignore the specified extension */
826 }
827
828 /* Allocate a buffer for the file name and extension */
829 if (lpExtension)
830 {
831 LPWSTR tmp;
832 DWORD len = wcslen(lpFileName) + wcslen(lpExtension);
833
834 if (!(tmp = RtlAllocateHeap(RtlGetProcessHeap(), 0, (len + 1) * sizeof(WCHAR))))
835 {
836 SetLastError(ERROR_OUTOFMEMORY);
837 return 0;
838 }
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);
844 }
845 }
846 else if (lpPath && lpPath[0]) /* search in the specified path */
847 {
848 ret = RtlDosSearchPath_U(lpPath,
849 lpFileName,
850 lpExtension,
851 nBufferLength * sizeof(WCHAR),
852 lpBuffer,
853 lpFilePart) / sizeof(WCHAR);
854 }
855 else /* search in the default path */
856 {
857 WCHAR *DllPath = GetDllLoadPath(NULL);
858
859 if (DllPath)
860 {
861 ret = RtlDosSearchPath_U(DllPath,
862 lpFileName,
863 lpExtension,
864 nBufferLength * sizeof(WCHAR),
865 lpBuffer,
866 lpFilePart) / sizeof(WCHAR);
867 RtlFreeHeap(RtlGetProcessHeap(), 0, DllPath);
868 }
869 else
870 {
871 SetLastError(ERROR_OUTOFMEMORY);
872 return 0;
873 }
874 }
875
876 if (!ret) SetLastError(ERROR_FILE_NOT_FOUND);
877
878 return ret;
879 }
880
881 /*
882 * @implemented
883 */
884 DWORD
885 WINAPI
886 GetLongPathNameW(IN LPCWSTR lpszShortPath,
887 IN LPWSTR lpszLongPath,
888 IN DWORD cchBuffer)
889 {
890 PWCHAR Path, Original, First, Last, Buffer, Src, Dst;
891 ULONG Length;
892 WCHAR LastChar;
893 HANDLE FindHandle;
894 DWORD ReturnLength;
895 ULONG ErrorMode;
896 BOOLEAN Found = FALSE;
897 WIN32_FIND_DATAW FindFileData;
898
899 /* Initialize so Quickie knows there's nothing to do */
900 Buffer = Original = NULL;
901 ReturnLength = 0;
902
903 /* First check if the input path was obviously NULL */
904 if (!lpszShortPath)
905 {
906 /* Fail the request */
907 SetLastError(ERROR_INVALID_PARAMETER);
908 return 0;
909 }
910
911 /* We will be touching removed, removable drives -- don't warn the user */
912 ErrorMode = SetErrorMode(SEM_NOOPENFILEERRORBOX | SEM_FAILCRITICALERRORS);
913
914 /* Do a simple check to see if the path exists */
915 if (GetFileAttributesW(lpszShortPath) == INVALID_FILE_ATTRIBUTES)
916 {
917 /* It doesn't, so fail */
918 ReturnLength = 0;
919 goto Quickie;
920 }
921
922 /* Now get a pointer to the actual path, skipping indicators */
923 Path = SkipPathTypeIndicator_U((LPWSTR)lpszShortPath);
924
925 /* Is there any path or filename in there? */
926 if (!(Path) ||
927 (*Path == UNICODE_NULL) ||
928 !(FindLFNorSFN_U(Path, &First, &Last, FALSE)))
929 {
930 /* There isn't, so the long path is simply the short path */
931 ReturnLength = wcslen(lpszShortPath);
932
933 /* Is there space for it? */
934 if ((cchBuffer > ReturnLength) && (lpszLongPath))
935 {
936 /* Make sure the pointers aren't already the same */
937 if (lpszLongPath != lpszShortPath)
938 {
939 /* They're not -- copy the short path into the long path */
940 RtlMoveMemory(lpszLongPath,
941 lpszShortPath,
942 ReturnLength * sizeof(WCHAR) + sizeof(UNICODE_NULL));
943 }
944 }
945 else
946 {
947 /* Otherwise, let caller know we need a bigger buffer, include NULL */
948 ReturnLength++;
949 }
950 goto Quickie;
951 }
952
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;
957
958 /* Make a copy of it */
959 RtlMoveMemory(Original, lpszShortPath, Length * sizeof(WCHAR));
960
961 /* Compute the new first and last markers */
962 First = &Original[First - lpszShortPath];
963 Last = &Original[Last - lpszShortPath];
964
965 /* Set the current destination pointer for a copy */
966 Dst = lpszLongPath;
967
968 /*
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.
972 *
973 * This is also why we use RtlMoveMemory everywhere. Don't use RtlCopyMemory!
974 */
975 if ((cchBuffer) && (lpszLongPath) &&
976 (((lpszLongPath >= lpszShortPath) && (lpszLongPath < &lpszShortPath[Length])) ||
977 ((lpszLongPath < lpszShortPath) && (&lpszLongPath[cchBuffer] >= lpszShortPath))))
978 {
979 Buffer = RtlAllocateHeap(RtlGetProcessHeap(), 0, cchBuffer * sizeof(WCHAR));
980 if (!Buffer) goto ErrorQuickie;
981
982 /* New destination */
983 Dst = Buffer;
984 }
985
986 /* Prepare for the loop */
987 Src = Original;
988 ReturnLength = 0;
989 while (TRUE)
990 {
991 /* Current delta in the loop */
992 Length = First - Src;
993
994 /* Update the return length by it */
995 ReturnLength += Length;
996
997 /* Is there a delta? If so, is there space and buffer for it? */
998 if ((Length) && (cchBuffer > ReturnLength) && (lpszLongPath))
999 {
1000 RtlMoveMemory(Dst, Src, Length * sizeof(WCHAR));
1001 Dst += Length;
1002 }
1003
1004 /* "Terminate" this portion of the path's substring so we can do a find */
1005 LastChar = *Last;
1006 *Last = UNICODE_NULL;
1007 FindHandle = FindFirstFileW(Original, &FindFileData);
1008 *Last = LastChar;
1009
1010 /* This portion wasn't found, so fail */
1011 if (FindHandle == INVALID_HANDLE_VALUE)
1012 {
1013 ReturnLength = 0;
1014 break;
1015 }
1016
1017 /* Close the find handle */
1018 FindClose(FindHandle);
1019
1020 /* Now check the length of the long name */
1021 Length = wcslen(FindFileData.cFileName);
1022 if (Length)
1023 {
1024 /* This is our new first marker */
1025 First = FindFileData.cFileName;
1026 }
1027 else
1028 {
1029 /* Otherwise, the name is the delta between our current markers */
1030 Length = Last - First;
1031 }
1032
1033 /* Update the return length with the short name length, if any */
1034 ReturnLength += Length;
1035
1036 /* Once again check for appropriate space and buffer */
1037 if ((cchBuffer > ReturnLength) && (lpszLongPath))
1038 {
1039 /* And do the copy if there is */
1040 RtlMoveMemory(Dst, First, Length * sizeof(WCHAR));
1041 Dst += Length;
1042 }
1043
1044 /* Now update the source pointer */
1045 Src = Last;
1046 if (*Src == UNICODE_NULL) break;
1047
1048 /* Are there more names in there? */
1049 Found = FindLFNorSFN_U(Src, &First, &Last, FALSE);
1050 if (!Found) break;
1051 }
1052
1053 /* The loop is done, is there anything left? */
1054 if (ReturnLength)
1055 {
1056 /* Get the length of the straggling path */
1057 Length = wcslen(Src);
1058 ReturnLength += Length;
1059
1060 /* Once again check for appropriate space and buffer */
1061 if ((cchBuffer > ReturnLength) && (lpszLongPath))
1062 {
1063 /* And do the copy if there is -- accounting for NULL here */
1064 RtlMoveMemory(Dst, Src, Length * sizeof(WCHAR) + sizeof(UNICODE_NULL));
1065
1066 /* What about our buffer? */
1067 if (Buffer)
1068 {
1069 /* Copy it into the caller's long path */
1070 RtlMoveMemory(lpszLongPath,
1071 Buffer,
1072 ReturnLength * sizeof(WCHAR) + sizeof(UNICODE_NULL));
1073 }
1074 }
1075 else
1076 {
1077 /* Buffer is too small, let the caller know, making space for NULL */
1078 ReturnLength++;
1079 }
1080 }
1081
1082 /* We're all done */
1083 goto Quickie;
1084
1085 ErrorQuickie:
1086 /* This is the goto for memory failures */
1087 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
1088
1089 Quickie:
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;
1095 }
1096
1097 /*
1098 * @implemented
1099 */
1100 DWORD
1101 WINAPI
1102 GetLongPathNameA(IN LPCSTR lpszShortPath,
1103 IN LPSTR lpszLongPath,
1104 IN DWORD cchBuffer)
1105 {
1106 ULONG Result, PathLength;
1107 PWCHAR LongPath;
1108 NTSTATUS Status;
1109 UNICODE_STRING LongPathUni, ShortPathUni;
1110 ANSI_STRING LongPathAnsi;
1111 WCHAR LongPathBuffer[MAX_PATH];
1112
1113 LongPath = NULL;
1114 LongPathAnsi.Buffer = NULL;
1115 ShortPathUni.Buffer = NULL;
1116 Result = 0;
1117
1118 if (!lpszShortPath)
1119 {
1120 SetLastError(ERROR_INVALID_PARAMETER);
1121 return 0;
1122 }
1123
1124 Status = Basep8BitStringToDynamicUnicodeString(&ShortPathUni, lpszShortPath);
1125 if (!NT_SUCCESS(Status)) goto Quickie;
1126
1127 LongPath = LongPathBuffer;
1128
1129 PathLength = GetLongPathNameW(ShortPathUni.Buffer, LongPathBuffer, MAX_PATH);
1130 if (PathLength >= MAX_PATH)
1131 {
1132 LongPath = RtlAllocateHeap(RtlGetProcessHeap(), 0, PathLength * sizeof(WCHAR));
1133 if (!LongPath)
1134 {
1135 PathLength = 0;
1136 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
1137 }
1138 else
1139 {
1140 PathLength = GetLongPathNameW(ShortPathUni.Buffer, LongPath, PathLength);
1141 }
1142 }
1143
1144 if (!PathLength) goto Quickie;
1145
1146 ShortPathUni.MaximumLength = PathLength * sizeof(WCHAR) + sizeof(UNICODE_NULL);
1147 LongPathUni.Buffer = LongPath;
1148 LongPathUni.Length = PathLength * sizeof(WCHAR);
1149
1150 Status = BasepUnicodeStringTo8BitString(&LongPathAnsi, &LongPathUni, TRUE);
1151 if (!NT_SUCCESS(Status))
1152 {
1153 BaseSetLastNTError(Status);
1154 Result = 0;
1155 }
1156
1157 Result = LongPathAnsi.Length;
1158 if ((lpszLongPath) && (cchBuffer > LongPathAnsi.Length))
1159 {
1160 RtlMoveMemory(lpszLongPath, LongPathAnsi.Buffer, LongPathAnsi.Length);
1161 lpszLongPath[Result] = ANSI_NULL;
1162 }
1163 else
1164 {
1165 Result = LongPathAnsi.Length + sizeof(ANSI_NULL);
1166 }
1167
1168 Quickie:
1169 if (ShortPathUni.Buffer) RtlFreeUnicodeString(&ShortPathUni);
1170 if (LongPathAnsi.Buffer) RtlFreeAnsiString(&LongPathAnsi);
1171 if ((LongPath) && (LongPath != LongPathBuffer))
1172 {
1173 RtlFreeHeap(RtlGetProcessHeap(), 0, LongPath);
1174 }
1175 return Result;
1176 }
1177
1178 /*
1179 * @implemented
1180 */
1181 DWORD
1182 WINAPI
1183 GetShortPathNameA(IN LPCSTR lpszLongPath,
1184 IN LPSTR lpszShortPath,
1185 IN DWORD cchBuffer)
1186 {
1187 ULONG Result, PathLength;
1188 PWCHAR ShortPath;
1189 NTSTATUS Status;
1190 UNICODE_STRING LongPathUni, ShortPathUni;
1191 ANSI_STRING ShortPathAnsi;
1192 WCHAR ShortPathBuffer[MAX_PATH];
1193
1194 ShortPath = NULL;
1195 ShortPathAnsi.Buffer = NULL;
1196 LongPathUni.Buffer = NULL;
1197 Result = 0;
1198
1199 if (!lpszLongPath)
1200 {
1201 SetLastError(ERROR_INVALID_PARAMETER);
1202 return 0;
1203 }
1204
1205 Status = Basep8BitStringToDynamicUnicodeString(&LongPathUni, lpszLongPath);
1206 if (!NT_SUCCESS(Status)) goto Quickie;
1207
1208 ShortPath = ShortPathBuffer;
1209
1210 PathLength = GetShortPathNameW(LongPathUni.Buffer, ShortPathBuffer, MAX_PATH);
1211 if (PathLength >= MAX_PATH)
1212 {
1213 ShortPath = RtlAllocateHeap(RtlGetProcessHeap(), 0, PathLength * sizeof(WCHAR));
1214 if (!ShortPath)
1215 {
1216 PathLength = 0;
1217 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
1218 }
1219 else
1220 {
1221 PathLength = GetShortPathNameW(LongPathUni.Buffer, ShortPath, PathLength);
1222 }
1223 }
1224
1225 if (!PathLength) goto Quickie;
1226
1227 LongPathUni.MaximumLength = PathLength * sizeof(WCHAR) + sizeof(UNICODE_NULL);
1228 ShortPathUni.Buffer = ShortPath;
1229 ShortPathUni.Length = PathLength * sizeof(WCHAR);
1230
1231 Status = BasepUnicodeStringTo8BitString(&ShortPathAnsi, &ShortPathUni, TRUE);
1232 if (!NT_SUCCESS(Status))
1233 {
1234 BaseSetLastNTError(Status);
1235 Result = 0;
1236 }
1237
1238 Result = ShortPathAnsi.Length;
1239 if ((lpszShortPath) && (cchBuffer > ShortPathAnsi.Length))
1240 {
1241 RtlMoveMemory(lpszShortPath, ShortPathAnsi.Buffer, ShortPathAnsi.Length);
1242 lpszShortPath[Result] = ANSI_NULL;
1243 }
1244 else
1245 {
1246 Result = ShortPathAnsi.Length + sizeof(ANSI_NULL);
1247 }
1248
1249 Quickie:
1250 if (LongPathUni.Buffer) RtlFreeUnicodeString(&LongPathUni);
1251 if (ShortPathAnsi.Buffer) RtlFreeAnsiString(&ShortPathAnsi);
1252 if ((ShortPath) && (ShortPath != ShortPathBuffer))
1253 {
1254 RtlFreeHeap(RtlGetProcessHeap(), 0, ShortPath);
1255 }
1256 return Result;
1257 }
1258
1259 /*
1260 * @implemented
1261 */
1262 DWORD
1263 WINAPI
1264 GetShortPathNameW(IN LPCWSTR lpszLongPath,
1265 IN LPWSTR lpszShortPath,
1266 IN DWORD cchBuffer)
1267 {
1268 PWCHAR Path, Original, First, Last, Buffer, Src, Dst;
1269 ULONG Length;
1270 WCHAR LastChar;
1271 HANDLE FindHandle;
1272 DWORD ReturnLength;
1273 ULONG ErrorMode;
1274 BOOLEAN Found = FALSE;
1275 WIN32_FIND_DATAW FindFileData;
1276
1277 /* Initialize so Quickie knows there's nothing to do */
1278 Buffer = Original = NULL;
1279 ReturnLength = 0;
1280
1281 /* First check if the input path was obviously NULL */
1282 if (!lpszLongPath)
1283 {
1284 /* Fail the request */
1285 SetLastError(ERROR_INVALID_PARAMETER);
1286 return 0;
1287 }
1288
1289 /* We will be touching removed, removable drives -- don't warn the user */
1290 ErrorMode = SetErrorMode(SEM_NOOPENFILEERRORBOX | SEM_FAILCRITICALERRORS);
1291
1292 /* Do a simple check to see if the path exists */
1293 if (GetFileAttributesW(lpszLongPath) == INVALID_FILE_ATTRIBUTES)
1294 {
1295 /* Windows checks for an application compatibility flag to allow this */
1296 if (!(NtCurrentPeb()) || !(NtCurrentPeb()->AppCompatFlags.LowPart & 1))
1297 {
1298 /* It doesn't, so fail */
1299 ReturnLength = 0;
1300 goto Quickie;
1301 }
1302 }
1303
1304 /* Now get a pointer to the actual path, skipping indicators */
1305 Path = SkipPathTypeIndicator_U((LPWSTR)lpszLongPath);
1306
1307 /* Is there any path or filename in there? */
1308 if (!(Path) ||
1309 (*Path == UNICODE_NULL) ||
1310 !(FindLFNorSFN_U(Path, &First, &Last, TRUE)))
1311 {
1312 /* There isn't, so the long path is simply the short path */
1313 ReturnLength = wcslen(lpszLongPath);
1314
1315 /* Is there space for it? */
1316 if ((cchBuffer > ReturnLength) && (lpszShortPath))
1317 {
1318 /* Make sure the pointers aren't already the same */
1319 if (lpszLongPath != lpszShortPath)
1320 {
1321 /* They're not -- copy the short path into the long path */
1322 RtlMoveMemory(lpszShortPath,
1323 lpszLongPath,
1324 ReturnLength * sizeof(WCHAR) + sizeof(UNICODE_NULL));
1325 }
1326 }
1327 else
1328 {
1329 /* Otherwise, let caller know we need a bigger buffer, include NULL */
1330 ReturnLength++;
1331 }
1332 goto Quickie;
1333 }
1334
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;
1339
1340 /* Make a copy of it */
1341 wcsncpy(Original, lpszLongPath, Length);
1342
1343 /* Compute the new first and last markers */
1344 First = &Original[First - lpszLongPath];
1345 Last = &Original[Last - lpszLongPath];
1346
1347 /* Set the current destination pointer for a copy */
1348 Dst = lpszShortPath;
1349
1350 /*
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.
1354 *
1355 * This is also why we use RtlMoveMemory everywhere. Don't use RtlCopyMemory!
1356 */
1357 if ((cchBuffer) && (lpszShortPath) &&
1358 (((lpszShortPath >= lpszLongPath) && (lpszShortPath < &lpszLongPath[Length])) ||
1359 ((lpszShortPath < lpszLongPath) && (&lpszShortPath[cchBuffer] >= lpszLongPath))))
1360 {
1361 Buffer = RtlAllocateHeap(RtlGetProcessHeap(), 0, cchBuffer * sizeof(WCHAR));
1362 if (!Buffer) goto ErrorQuickie;
1363
1364 /* New destination */
1365 Dst = Buffer;
1366 }
1367
1368 /* Prepare for the loop */
1369 Src = Original;
1370 ReturnLength = 0;
1371 while (TRUE)
1372 {
1373 /* Current delta in the loop */
1374 Length = First - Src;
1375
1376 /* Update the return length by it */
1377 ReturnLength += Length;
1378
1379 /* Is there a delta? If so, is there space and buffer for it? */
1380 if ((Length) && (cchBuffer > ReturnLength) && (lpszShortPath))
1381 {
1382 RtlMoveMemory(Dst, Src, Length * sizeof(WCHAR));
1383 Dst += Length;
1384 }
1385
1386 /* "Terminate" this portion of the path's substring so we can do a find */
1387 LastChar = *Last;
1388 *Last = UNICODE_NULL;
1389 FindHandle = FindFirstFileW(Original, &FindFileData);
1390 *Last = LastChar;
1391
1392 /* This portion wasn't found, so fail */
1393 if (FindHandle == INVALID_HANDLE_VALUE)
1394 {
1395 ReturnLength = 0;
1396 break;
1397 }
1398
1399 /* Close the find handle */
1400 FindClose(FindHandle);
1401
1402 /* Now check the length of the short name */
1403 Length = wcslen(FindFileData.cAlternateFileName);
1404 if (Length)
1405 {
1406 /* This is our new first marker */
1407 First = FindFileData.cAlternateFileName;
1408 }
1409 else
1410 {
1411 /* Otherwise, the name is the delta between our current markers */
1412 Length = Last - First;
1413 }
1414
1415 /* Update the return length with the short name length, if any */
1416 ReturnLength += Length;
1417
1418 /* Once again check for appropriate space and buffer */
1419 if ((cchBuffer > ReturnLength) && (lpszShortPath))
1420 {
1421 /* And do the copy if there is */
1422 RtlMoveMemory(Dst, First, Length * sizeof(WCHAR));
1423 Dst += Length;
1424 }
1425
1426 /* Now update the source pointer */
1427 Src = Last;
1428 if (*Src == UNICODE_NULL) break;
1429
1430 /* Are there more names in there? */
1431 Found = FindLFNorSFN_U(Src, &First, &Last, TRUE);
1432 if (!Found) break;
1433 }
1434
1435 /* The loop is done, is there anything left? */
1436 if (ReturnLength)
1437 {
1438 /* Get the length of the straggling path */
1439 Length = wcslen(Src);
1440 ReturnLength += Length;
1441
1442 /* Once again check for appropriate space and buffer */
1443 if ((cchBuffer > ReturnLength) && (lpszShortPath))
1444 {
1445 /* And do the copy if there is -- accounting for NULL here */
1446 RtlMoveMemory(Dst, Src, Length * sizeof(WCHAR) + sizeof(UNICODE_NULL));
1447
1448 /* What about our buffer? */
1449 if (Buffer)
1450 {
1451 /* Copy it into the caller's long path */
1452 RtlMoveMemory(lpszShortPath,
1453 Buffer,
1454 ReturnLength * sizeof(WCHAR) + sizeof(UNICODE_NULL));
1455 }
1456 }
1457 else
1458 {
1459 /* Buffer is too small, let the caller know, making space for NULL */
1460 ReturnLength++;
1461 }
1462 }
1463
1464 /* We're all done */
1465 goto Quickie;
1466
1467 ErrorQuickie:
1468 /* This is the goto for memory failures */
1469 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
1470
1471 Quickie:
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;
1477 }
1478
1479 /*
1480 * @implemented
1481 */
1482 DWORD
1483 WINAPI
1484 GetCurrentDirectoryA(IN DWORD nBufferLength,
1485 IN LPSTR lpBuffer)
1486 {
1487 WCHAR BufferW[MAX_PATH];
1488 DWORD ret;
1489
1490 ret = GetCurrentDirectoryW(MAX_PATH, BufferW);
1491
1492 if (!ret) return 0;
1493 if (ret > MAX_PATH)
1494 {
1495 SetLastError(ERROR_FILENAME_EXCED_RANGE);
1496 return 0;
1497 }
1498
1499 return FilenameW2A_FitOrFail(lpBuffer, nBufferLength, BufferW, ret+1);
1500 }
1501
1502 /*
1503 * @implemented
1504 */
1505 DWORD
1506 WINAPI
1507 GetCurrentDirectoryW(IN DWORD nBufferLength,
1508 IN LPWSTR lpBuffer)
1509 {
1510 ULONG Length;
1511
1512 Length = RtlGetCurrentDirectory_U (nBufferLength * sizeof(WCHAR), lpBuffer);
1513 return (Length / sizeof (WCHAR));
1514 }
1515
1516 /*
1517 * @implemented
1518 */
1519 BOOL
1520 WINAPI
1521 SetCurrentDirectoryA(IN LPCSTR lpPathName)
1522 {
1523 PWCHAR PathNameW;
1524
1525 DPRINT("setcurrdir: %s\n",lpPathName);
1526
1527 if (!(PathNameW = FilenameA2W(lpPathName, FALSE))) return FALSE;
1528
1529 return SetCurrentDirectoryW(PathNameW);
1530 }
1531
1532 /*
1533 * @implemented
1534 */
1535 BOOL
1536 WINAPI
1537 SetCurrentDirectoryW(IN LPCWSTR lpPathName)
1538 {
1539 UNICODE_STRING UnicodeString;
1540 NTSTATUS Status;
1541
1542 RtlInitUnicodeString(&UnicodeString, lpPathName);
1543
1544 Status = RtlSetCurrentDirectory_U(&UnicodeString);
1545 if (!NT_SUCCESS(Status))
1546 {
1547 BaseSetLastNTError (Status);
1548 return FALSE;
1549 }
1550
1551 return TRUE;
1552 }
1553
1554 /*
1555 * @implemented
1556 *
1557 * NOTE: Windows returns a dos/short (8.3) path
1558 */
1559 DWORD
1560 WINAPI
1561 GetTempPathA(IN DWORD nBufferLength,
1562 IN LPSTR lpBuffer)
1563 {
1564 WCHAR BufferW[MAX_PATH];
1565 DWORD ret;
1566
1567 ret = GetTempPathW(MAX_PATH, BufferW);
1568
1569 if (!ret) return 0;
1570
1571 if (ret > MAX_PATH)
1572 {
1573 SetLastError(ERROR_FILENAME_EXCED_RANGE);
1574 return 0;
1575 }
1576
1577 return FilenameW2A_FitOrFail(lpBuffer, nBufferLength, BufferW, ret+1);
1578 }
1579
1580 /*
1581 * @implemented
1582 *
1583 * ripped from wine
1584 */
1585 DWORD
1586 WINAPI
1587 GetTempPathW(IN DWORD count,
1588 IN LPWSTR path)
1589 {
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];
1594 UINT ret;
1595
1596 DPRINT("%u,%p\n", count, path);
1597
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 )))
1602 return 0;
1603
1604 if (ret > MAX_PATH)
1605 {
1606 SetLastError(ERROR_FILENAME_EXCED_RANGE);
1607 return 0;
1608 }
1609
1610 ret = GetFullPathNameW(tmp_path, MAX_PATH, tmp_path, NULL);
1611 if (!ret) return 0;
1612
1613 if (ret > MAX_PATH - 2)
1614 {
1615 SetLastError(ERROR_FILENAME_EXCED_RANGE);
1616 return 0;
1617 }
1618
1619 if (tmp_path[ret-1] != '\\')
1620 {
1621 tmp_path[ret++] = '\\';
1622 tmp_path[ret] = '\0';
1623 }
1624
1625 ret++; /* add space for terminating 0 */
1626
1627 if (count)
1628 {
1629 lstrcpynW(path, tmp_path, count);
1630 if (count >= ret)
1631 ret--; /* return length without 0 */
1632 else if (count < 4)
1633 path[0] = 0; /* avoid returning ambiguous "X:" */
1634 }
1635
1636 DPRINT("GetTempPathW returning %u, %S\n", ret, path);
1637 return ret;
1638 }
1639
1640 /*
1641 * @implemented
1642 */
1643 UINT
1644 WINAPI
1645 GetSystemDirectoryA(IN LPSTR lpBuffer,
1646 IN UINT uSize)
1647 {
1648 return FilenameU2A_FitOrFail(lpBuffer, uSize, &SystemDirectory);
1649 }
1650
1651 /*
1652 * @implemented
1653 */
1654 UINT
1655 WINAPI
1656 GetSystemDirectoryW(IN LPWSTR lpBuffer,
1657 IN UINT uSize)
1658 {
1659 ULONG Length;
1660
1661 Length = SystemDirectory.Length / sizeof (WCHAR);
1662
1663 if (lpBuffer == NULL) return Length + 1;
1664
1665 if (uSize > Length)
1666 {
1667 memmove(lpBuffer, SystemDirectory.Buffer, SystemDirectory.Length);
1668 lpBuffer[Length] = 0;
1669
1670 return Length; //good: ret chars excl. nullchar
1671 }
1672
1673 return Length+1; //bad: ret space needed incl. nullchar
1674 }
1675
1676 /*
1677 * @implemented
1678 */
1679 UINT
1680 WINAPI
1681 GetWindowsDirectoryA(IN LPSTR lpBuffer,
1682 IN UINT uSize)
1683 {
1684 return FilenameU2A_FitOrFail(lpBuffer, uSize, &WindowsDirectory);
1685 }
1686
1687 /*
1688 * @implemented
1689 */
1690 UINT
1691 WINAPI
1692 GetWindowsDirectoryW(IN LPWSTR lpBuffer,
1693 IN UINT uSize)
1694 {
1695 ULONG Length;
1696
1697 Length = WindowsDirectory.Length / sizeof (WCHAR);
1698
1699 if (lpBuffer == NULL) return Length + 1;
1700
1701 if (uSize > Length)
1702 {
1703 memmove(lpBuffer, WindowsDirectory.Buffer, WindowsDirectory.Length);
1704 lpBuffer[Length] = 0;
1705
1706 return Length; //good: ret chars excl. nullchar
1707 }
1708
1709 return Length+1; //bad: ret space needed incl. nullchar
1710 }
1711
1712 /*
1713 * @implemented
1714 */
1715 UINT
1716 WINAPI
1717 GetSystemWindowsDirectoryA(IN LPSTR lpBuffer,
1718 IN UINT uSize)
1719 {
1720 return GetWindowsDirectoryA(lpBuffer, uSize);
1721 }
1722
1723 /*
1724 * @implemented
1725 */
1726 UINT
1727 WINAPI
1728 GetSystemWindowsDirectoryW(IN LPWSTR lpBuffer,
1729 IN UINT uSize)
1730 {
1731 return GetWindowsDirectoryW(lpBuffer, uSize);
1732 }
1733
1734 /*
1735 * @unimplemented
1736 */
1737 UINT
1738 WINAPI
1739 GetSystemWow64DirectoryW(IN LPWSTR lpBuffer,
1740 IN UINT uSize)
1741 {
1742 #ifdef _WIN64
1743 UNIMPLEMENTED;
1744 return 0;
1745 #else
1746 SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
1747 return 0;
1748 #endif
1749 }
1750
1751 /*
1752 * @unimplemented
1753 */
1754 UINT
1755 WINAPI
1756 GetSystemWow64DirectoryA(IN LPSTR lpBuffer,
1757 IN UINT uSize)
1758 {
1759 #ifdef _WIN64
1760 UNIMPLEMENTED;
1761 return 0;
1762 #else
1763 SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
1764 return 0;
1765 #endif
1766 }
1767
1768 /* EOF */