[KERNEL32]: Implement BasepComputeProcessPath.
[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 NoDefaultCurrentDirectoryInExePath = RTL_CONSTANT_STRING(L"NoDefaultCurrentDirectoryInExePath");
19
20 UNICODE_STRING BaseWindowsSystemDirectory, BaseWindowsDirectory;
21 UNICODE_STRING BaseDefaultPathAppend, BaseDefaultPath, BaseDllDirectory;
22
23 PVOID gpTermsrvGetWindowsDirectoryA;
24 PVOID gpTermsrvGetWindowsDirectoryW;
25
26 /* This is bitmask for each illegal filename character */
27 /* If someone has time, please feel free to use 0b notation */
28 DWORD IllegalMask[4] =
29 {
30 0xFFFFFFFF, // None allowed (00 to 1F)
31 0xFC009C05, // 20, 22, 2A, 2B, 2C, 2F, 3A, 3B, 3C, 3D, 3E, 3F not allowed
32 0x38000000, // 5B, 5C, 5D not allowed
33 0x10000000 // 7C not allowed
34 };
35
36 BASE_SEARCH_PATH_TYPE BaseDllOrderCurrent[BaseCurrentDirPlacementMax][BaseSearchPathMax] =
37 {
38 {
39 BaseSearchPathApp,
40 BaseSearchPathCurrent,
41 BaseSearchPathDefault,
42 BaseSearchPathEnv,
43 BaseSearchPathInvalid
44 },
45 {
46 BaseSearchPathApp,
47 BaseSearchPathDefault,
48 BaseSearchPathCurrent,
49 BaseSearchPathEnv,
50 BaseSearchPathInvalid
51 }
52 };
53
54 BASE_SEARCH_PATH_TYPE BaseProcessOrderNoCurrent[BaseSearchPathMax] =
55 {
56 BaseSearchPathApp,
57 BaseSearchPathDefault,
58 BaseSearchPathEnv,
59 BaseSearchPathInvalid,
60 BaseSearchPathInvalid
61 };
62
63 BASE_SEARCH_PATH_TYPE BaseDllOrderNoCurrent[BaseSearchPathMax] =
64 {
65 BaseSearchPathApp,
66 BaseSearchPathDll,
67 BaseSearchPathDefault,
68 BaseSearchPathEnv,
69 BaseSearchPathInvalid
70 };
71
72 BASE_SEARCH_PATH_TYPE BaseProcessOrder[BaseSearchPathMax] =
73 {
74 BaseSearchPathApp,
75 BaseSearchPathCurrent,
76 BaseSearchPathDefault,
77 BaseSearchPathEnv,
78 BaseSearchPathInvalid
79 };
80
81 BASE_CURRENT_DIR_PLACEMENT BasepDllCurrentDirPlacement = BaseCurrentDirPlacementInvalid;
82
83 extern UNICODE_STRING BasePathVariableName;
84
85 /* PRIVATE FUNCTIONS **********************************************************/
86
87 PWCHAR
88 WINAPI
89 BasepEndOfDirName(IN PWCHAR FileName)
90 {
91 PWCHAR FileNameEnd, FileNameSeparator;
92
93 /* Find the first slash */
94 FileNameSeparator = wcschr(FileName, OBJ_NAME_PATH_SEPARATOR);
95 if (FileNameSeparator)
96 {
97 /* Find the last one */
98 FileNameEnd = wcsrchr(FileNameSeparator, OBJ_NAME_PATH_SEPARATOR);
99 ASSERT(FileNameEnd);
100
101 /* Handle the case where they are one and the same */
102 if (FileNameEnd == FileNameSeparator) FileNameEnd++;
103 }
104 else
105 {
106 /* No directory was specified */
107 FileNameEnd = NULL;
108 }
109
110 /* Return where the directory ends and the filename starts */
111 return FileNameEnd;
112 }
113
114 LPWSTR
115 WINAPI
116 BasepComputeProcessPath(IN PBASE_SEARCH_PATH_TYPE PathOrder,
117 IN LPWSTR AppName,
118 IN LPVOID Environment)
119 {
120 PWCHAR PathBuffer, Buffer, AppNameEnd, PathCurrent;
121 ULONG PathLengthInBytes;
122 NTSTATUS Status;
123 UNICODE_STRING EnvPath;
124 PBASE_SEARCH_PATH_TYPE Order;
125
126 /* Initialize state */
127 AppNameEnd = Buffer = PathBuffer = NULL;
128 Status = STATUS_SUCCESS;
129 PathLengthInBytes = 0;
130
131 /* Loop the ordering array */
132 for (Order = PathOrder; *Order != BaseSearchPathInvalid; Order++) {
133 switch (*Order)
134 {
135 /* Compute the size of the DLL path */
136 case BaseSearchPathDll:
137
138 /* This path only gets called if SetDllDirectory was called */
139 ASSERT(BaseDllDirectory.Buffer != NULL);
140
141 /* Make sure there's a DLL directory size */
142 if (BaseDllDirectory.Length)
143 {
144 /* Add it, plus the separator */
145 PathLengthInBytes += BaseDllDirectory.Length + sizeof(L';');
146 }
147 break;
148
149 /* Compute the size of the current path */
150 case BaseSearchPathCurrent:
151
152 /* Add ".;" */
153 PathLengthInBytes += (2 * sizeof(WCHAR));
154 break;
155
156 /* Compute the size of the "PATH" environment variable */
157 case BaseSearchPathEnv:
158
159 /* Grab PEB lock if one wasn't passed in */
160 if (!Environment) RtlAcquirePebLock();
161
162 /* Query the size first */
163 EnvPath.MaximumLength = 0;
164 Status = RtlQueryEnvironmentVariable_U(Environment,
165 &BasePathVariableName,
166 &EnvPath);
167 if (Status == STATUS_BUFFER_TOO_SMALL)
168 {
169 /* Compute the size we'll need for the environment */
170 EnvPath.MaximumLength = EnvPath.Length + sizeof(WCHAR);
171 if ((EnvPath.Length + sizeof(WCHAR)) > UNICODE_STRING_MAX_BYTES)
172 {
173 /* Don't let it overflow */
174 EnvPath.MaximumLength = EnvPath.Length;
175 }
176
177 /* Allocate the environment buffer */
178 Buffer = RtlAllocateHeap(RtlGetProcessHeap(),
179 0,
180 EnvPath.MaximumLength);
181 if (Buffer)
182 {
183 /* Now query the PATH environment variable */
184 EnvPath.Buffer = Buffer;
185 Status = RtlQueryEnvironmentVariable_U(Environment,
186 &BasePathVariableName,
187 &EnvPath);
188 }
189 else
190 {
191 /* Failure case */
192 Status = STATUS_NO_MEMORY;
193 }
194 }
195
196 /* Release the PEB lock from above */
197 if (!Environment) RtlReleasePebLock();
198
199 /* There might not be a PATH */
200 if (Status == STATUS_VARIABLE_NOT_FOUND)
201 {
202 /* In this case, skip this PathOrder */
203 EnvPath.Length = EnvPath.MaximumLength = 0;
204 Status = STATUS_SUCCESS;
205 }
206 else if (!NT_SUCCESS(Status))
207 {
208 /* An early failure, go to exit code */
209 goto Quickie;
210 }
211 else
212 {
213 /* Add the length of the PATH variable */
214 ASSERT(!(EnvPath.Length & 1));
215 PathLengthInBytes += (EnvPath.Length + sizeof(L';'));
216 }
217 break;
218
219 /* Compute the size of the default search path */
220 case BaseSearchPathDefault:
221
222 /* Just add it... it already has a ';' at the end */
223 ASSERT(!(BaseDefaultPath.Length & 1));
224 PathLengthInBytes += BaseDefaultPath.Length;
225 break;
226
227 /* Compute the size of the current app directory */
228 case BaseSearchPathApp:
229 /* Find out where the app name ends, to get only the directory */
230 if (AppName) AppNameEnd = BasepEndOfDirName(AppName);
231
232 /* Check if there was no application name passed in */
233 if (!(AppName) || !(AppNameEnd))
234 {
235 /* Do we have a per-thread CURDIR to use? */
236 if (NtCurrentTeb()->NtTib.SubSystemTib)
237 {
238 /* This means someone added RTL_PERTHREAD_CURDIR */
239 UNIMPLEMENTED;
240 while (TRUE);
241 }
242
243 /* We do not. Do we have the LDR_ENTRY for the executable? */
244 if (!BasepExeLdrEntry)
245 {
246 /* We do not. Grab it */
247 LdrEnumerateLoadedModules(0,
248 BasepLocateExeLdrEntry,
249 NtCurrentPeb()->ImageBaseAddress);
250 }
251
252 /* Now do we have it? */
253 if (BasepExeLdrEntry)
254 {
255 /* Yes, so read the name out of it */
256 AppName = BasepExeLdrEntry->FullDllName.Buffer;
257 }
258
259 /* Find out where the app name ends, to get only the directory */
260 if (AppName) AppNameEnd = BasepEndOfDirName(AppName);
261 }
262
263 /* So, do we have an application name and its directory? */
264 if ((AppName) && (AppNameEnd))
265 {
266 /* Add the size of the app's directory, plus the separator */
267 PathLengthInBytes += ((AppNameEnd - AppName) * sizeof(WCHAR)) + sizeof(L';');
268 }
269 break;
270
271 default:
272 break;
273 }
274 }
275
276 /* Bam, all done, we now have the final path size */
277 ASSERT(PathLengthInBytes > 0);
278 ASSERT(!(PathLengthInBytes & 1));
279
280 /* Allocate the buffer to hold it */
281 PathBuffer = RtlAllocateHeap(RtlGetProcessHeap(), 0, PathLengthInBytes);
282 if (!PathBuffer)
283 {
284 /* Failure path */
285 Status = STATUS_NO_MEMORY;
286 goto Quickie;
287 }
288
289 /* Now we loop again, this time to copy the data */
290 PathCurrent = PathBuffer;
291 for (Order = PathOrder; *Order != BaseSearchPathInvalid; Order++) {
292 switch (*Order)
293 {
294 /* Add the DLL path */
295 case BaseSearchPathDll:
296 if (BaseDllDirectory.Length)
297 {
298 /* Copy it in the buffer, ASSERT there's enough space */
299 ASSERT((((PathCurrent - PathBuffer + 1) * sizeof(WCHAR)) + BaseDllDirectory.Length) <= PathLengthInBytes);
300 RtlCopyMemory(PathCurrent,
301 BaseDllDirectory.Buffer,
302 BaseDllDirectory.Length);
303
304 /* Update the current pointer, add a separator */
305 PathCurrent += (BaseDllDirectory.Length / sizeof(WCHAR));
306 *PathCurrent++ = ';';
307 }
308 break;
309
310 /* Add the current applicaiton path */
311 case BaseSearchPathApp:
312 if ((AppName) && (AppNameEnd))
313 {
314 /* Copy it in the buffer, ASSERT there's enough space */
315 ASSERT(((PathCurrent - PathBuffer + 1 + (AppNameEnd - AppName)) * sizeof(WCHAR)) <= PathLengthInBytes);
316 RtlCopyMemory(PathCurrent,
317 AppName,
318 (AppNameEnd - AppName) * sizeof(WCHAR));
319
320 /* Update the current pointer, add a separator */
321 PathCurrent += AppNameEnd - AppName;
322 *PathCurrent++ = ';';
323 }
324 break;
325
326 /* Add the default search path */
327 case BaseSearchPathDefault:
328 /* Copy it in the buffer, ASSERT there's enough space */
329 ASSERT((((PathCurrent - PathBuffer) * sizeof(WCHAR)) + BaseDefaultPath.Length) <= PathLengthInBytes);
330 RtlCopyMemory(PathCurrent, BaseDefaultPath.Buffer, BaseDefaultPath.Length);
331
332 /* Update the current pointer. The default path already has a ";" */
333 PathCurrent += (BaseDefaultPath.Length / sizeof(WCHAR));
334 break;
335
336 /* Add the path in the PATH environment variable */
337 case BaseSearchPathEnv:
338 if (EnvPath.Length)
339 {
340 /* Copy it in the buffer, ASSERT there's enough space */
341 ASSERT((((PathCurrent - PathBuffer + 1) * sizeof(WCHAR)) + EnvPath.Length) <= PathLengthInBytes);
342 RtlCopyMemory(PathCurrent, EnvPath.Buffer, EnvPath.Length);
343
344 /* Update the current pointer, add a separator */
345 PathCurrent += (EnvPath.Length / sizeof(WCHAR));
346 *PathCurrent++ = ';';
347 }
348 break;
349
350 /* Add the current dierctory */
351 case BaseSearchPathCurrent:
352
353 /* Copy it in the buffer, ASSERT there's enough space */
354 ASSERT(((PathCurrent - PathBuffer + 2) * sizeof(WCHAR)) <= PathLengthInBytes);
355 *PathCurrent++ = '.';
356
357 /* Add the path separator */
358 *PathCurrent++ = ';';
359 break;
360
361 default:
362 break;
363 }
364 }
365
366 /* Everything should've perfectly fit in there */
367 ASSERT((PathCurrent - PathBuffer) * sizeof(WCHAR) == PathLengthInBytes);
368 ASSERT(PathCurrent > PathBuffer);
369
370 /* Terminate the whole thing */
371 PathCurrent[-1] = UNICODE_NULL;
372
373 Quickie:
374 /* Exit path: free our buffers */
375 if (Buffer) RtlFreeHeap(RtlGetProcessHeap(), 0, Buffer);
376 if (PathBuffer)
377 {
378 /* This only gets freed in the failure path, since caller wants it */
379 if (!NT_SUCCESS(Status))
380 {
381 RtlFreeHeap(RtlGetProcessHeap(), 0, PathBuffer);
382 PathBuffer = NULL;
383 }
384 }
385
386 /* Return the path! */
387 return PathBuffer;
388 }
389
390 LPWSTR
391 WINAPI
392 BaseComputeProcessSearchPath(VOID)
393 {
394 DPRINT1("Computing Process Search path\n");
395
396 /* Compute the path using default process order */
397 return BasepComputeProcessPath(BaseProcessOrder, NULL, NULL);
398 }
399
400 LPWSTR
401 WINAPI
402 BaseComputeProcessExePath(IN LPWSTR FullPath)
403 {
404 PBASE_SEARCH_PATH_TYPE PathOrder;
405 DPRINT1("Computing EXE path: %wZ\n", FullPath);
406
407 /* Check if we should use the current directory */
408 PathOrder = NeedCurrentDirectoryForExePathW(FullPath) ?
409 BaseProcessOrder : BaseProcessOrderNoCurrent;
410
411 /* And now compute the path */
412 return BasepComputeProcessPath(PathOrder, NULL, NULL);
413 }
414
415 LPWSTR
416 WINAPI
417 BaseComputeProcessDllPath(IN LPWSTR FullPath,
418 IN PVOID Environment)
419 {
420 LPWSTR DllPath = NULL;
421 UNICODE_STRING KeyName = RTL_CONSTANT_STRING(L"\\Registry\\MACHINE\\System\\CurrentControlSet\\Control\\Session Manager");
422 UNICODE_STRING ValueName = RTL_CONSTANT_STRING(L"SafeDllSearchMode");
423 OBJECT_ATTRIBUTES ObjectAttributes = RTL_CONSTANT_OBJECT_ATTRIBUTES(&KeyName, OBJ_CASE_INSENSITIVE);
424 KEY_VALUE_PARTIAL_INFORMATION PartialInfo;
425 HANDLE KeyHandle;
426 NTSTATUS Status;
427 ULONG ResultLength;
428 BASE_CURRENT_DIR_PLACEMENT CurrentDirPlacement, OldCurrentDirPlacement;
429
430 /* Acquire DLL directory lock */
431 RtlEnterCriticalSection(&BaseDllDirectoryLock);
432
433 /* Check if we have a base dll directory */
434 if (BaseDllDirectory.Buffer)
435 {
436 /* Then compute the process path using DLL order (without curdir) */
437 DllPath = BasepComputeProcessPath(BaseDllOrderNoCurrent, FullPath, Environment);
438
439 /* Release DLL directory lock */
440 RtlLeaveCriticalSection(&BaseDllDirectoryLock);
441
442 /* Return dll path */
443 return DllPath;
444 }
445
446 /* Release DLL directory lock */
447 RtlLeaveCriticalSection(&BaseDllDirectoryLock);
448
449 /* Read the current placement */
450 CurrentDirPlacement = BasepDllCurrentDirPlacement;
451 if (CurrentDirPlacement == BaseCurrentDirPlacementInvalid)
452 {
453 /* Open the configuration key */
454 Status = NtOpenKey(&KeyHandle, KEY_QUERY_VALUE, &ObjectAttributes);
455 if (NT_SUCCESS(Status))
456 {
457 /* Query if safe search is enabled */
458 Status = NtQueryValueKey(KeyHandle,
459 &ValueName,
460 KeyValuePartialInformation,
461 &PartialInfo,
462 sizeof(PartialInfo),
463 &ResultLength);
464 if (NT_SUCCESS(Status))
465 {
466 /* Read the value if the size is OK */
467 if (ResultLength == sizeof(PartialInfo))
468 {
469 CurrentDirPlacement = *(PULONG)PartialInfo.Data;
470 }
471 }
472
473 /* Close the handle */
474 NtClose(KeyHandle);
475
476 /* Validate the registry value */
477 if ((CurrentDirPlacement <= BaseCurrentDirPlacementInvalid) ||
478 (CurrentDirPlacement >= BaseCurrentDirPlacementMax))
479 {
480 /* Default to safe search */
481 CurrentDirPlacement = BaseCurrentDirPlacementSafe;
482 }
483 }
484
485 /* Update the placement and read the old one */
486 OldCurrentDirPlacement = InterlockedCompareExchange((PLONG)&BasepDllCurrentDirPlacement,
487 CurrentDirPlacement,
488 BaseCurrentDirPlacementInvalid);
489 if (OldCurrentDirPlacement != BaseCurrentDirPlacementInvalid)
490 {
491 /* If there already was a placement, use it */
492 CurrentDirPlacement = OldCurrentDirPlacement;
493 }
494 }
495
496 /* Check if the placement is invalid or not set */
497 if ((CurrentDirPlacement <= BaseCurrentDirPlacementInvalid) ||
498 (CurrentDirPlacement >= BaseCurrentDirPlacementMax))
499 {
500 /* Default to safe search */
501 CurrentDirPlacement = BaseCurrentDirPlacementSafe;
502 }
503
504 /* Compute the process path using either normal or safe search */
505 DllPath = BasepComputeProcessPath(BaseDllOrderCurrent[CurrentDirPlacement],
506 FullPath,
507 Environment);
508
509 /* Return dll path */
510 return DllPath;
511 }
512
513 BOOLEAN
514 WINAPI
515 CheckForSameCurdir(IN PUNICODE_STRING DirName)
516 {
517 PUNICODE_STRING CurDir;
518 USHORT CurLength;
519 BOOLEAN Result;
520 UNICODE_STRING CurDirCopy;
521
522 CurDir = &NtCurrentPeb()->ProcessParameters->CurrentDirectory.DosPath;
523
524 CurLength = CurDir->Length;
525 if (CurDir->Length <= 6)
526 {
527 if (CurLength != DirName->Length) return FALSE;
528 }
529 else
530 {
531 if ((CurLength - 2) != DirName->Length) return FALSE;
532 }
533
534 RtlAcquirePebLock();
535
536 CurDirCopy = *CurDir;
537 if (CurDirCopy.Length > 6) CurDirCopy.Length -= 2;
538
539 Result = 0;
540
541 if (RtlEqualUnicodeString(&CurDirCopy, DirName, TRUE)) Result = TRUE;
542
543 RtlReleasePebLock();
544
545 return Result;
546 }
547
548 /*
549 * Why not use RtlIsNameLegalDOS8Dot3? In fact the actual algorithm body is
550 * identical (other than the Rtl can optionally check for spaces), however the
551 * Rtl will always convert to OEM, while kernel32 has two possible file modes
552 * (ANSI or OEM). Therefore we must duplicate the algorithm body to get
553 * the correct compatible results
554 */
555 BOOL
556 WINAPI
557 IsShortName_U(IN PWCHAR Name,
558 IN ULONG Length)
559 {
560 BOOLEAN HasExtension;
561 WCHAR c;
562 NTSTATUS Status;
563 UNICODE_STRING UnicodeName;
564 ANSI_STRING AnsiName;
565 ULONG i, Dots;
566 CHAR AnsiBuffer[MAX_PATH];
567 ASSERT(Name);
568
569 /* What do you think 8.3 means? */
570 if (Length > 12) return FALSE;
571
572 /* Sure, any emtpy name is a short name */
573 if (!Length) return TRUE;
574
575 /* This could be . or .. or something else */
576 if (*Name == L'.')
577 {
578 /* Which one is it */
579 if ((Length == 1) || ((Length == 2) && *(Name + 1) == L'.'))
580 {
581 /* . or .., this is good */
582 return TRUE;
583 }
584
585 /* Some other bizare dot-based name, not good */
586 return FALSE;
587 }
588
589 /* Initialize our two strings */
590 RtlInitEmptyAnsiString(&AnsiName, AnsiBuffer, MAX_PATH);
591 RtlInitEmptyUnicodeString(&UnicodeName, Name, Length * sizeof(WCHAR));
592 UnicodeName.Length = UnicodeName.MaximumLength;
593
594 /* Now do the conversion */
595 Status = BasepUnicodeStringTo8BitString(&AnsiName, &UnicodeName, FALSE);
596 if (!NT_SUCCESS(Status)) return FALSE;
597
598 /* Now we loop the name */
599 HasExtension = FALSE;
600 for (i = 0, Dots = Length - 1; i < AnsiName.Length; i++, Dots--)
601 {
602 /* Read the current byte */
603 c = AnsiName.Buffer[i];
604
605 /* Is it DBCS? */
606 if (IsDBCSLeadByte(c))
607 {
608 /* If we're near the end of the string, we can't allow a DBCS */
609 if ((!(HasExtension) && (i >= 7)) || (i == AnsiName.Length - 1))
610 {
611 return FALSE;
612 }
613
614 /* Otherwise we skip over it */
615 continue;
616 }
617
618 /* Check for illegal characters */
619 if ((c > 0x7F) || (IllegalMask[c / 32] & (1 << (c % 32))))
620 {
621 return FALSE;
622 }
623
624 /* Check if this is perhaps an extension? */
625 if (c == '.')
626 {
627 /* Unless the extension is too large or there's more than one */
628 if ((HasExtension) || (Dots > 3)) return FALSE;
629
630 /* This looks like an extension */
631 HasExtension = TRUE;
632 }
633
634 /* 8.3 length was validated, but now we must guard against 9.2 or similar */
635 if ((i >= 8) && !(HasExtension)) return FALSE;
636 }
637
638 /* You survived the loop, this is a good short name */
639 return TRUE;
640 }
641
642 BOOL
643 WINAPI
644 IsLongName_U(IN PWCHAR FileName,
645 IN ULONG Length)
646 {
647 BOOLEAN HasExtension;
648 ULONG i, Dots;
649
650 /* More than 8.3, any combination of dots, and NULL names are all long */
651 if (!(Length) || (Length > 12) || (*FileName == L'.')) return TRUE;
652
653 /* Otherwise, initialize our scanning loop */
654 HasExtension = FALSE;
655 for (i = 0, Dots = Length - 1; i < Length; i++, Dots--)
656 {
657 /* Check if this could be an extension */
658 if (FileName[i] == L'.')
659 {
660 /* Unlike the short case, we WANT more than one extension, or a long one */
661 if ((HasExtension) || (Dots > 3))
662 {
663 return TRUE;
664 }
665 HasExtension = TRUE;
666 }
667
668 /* Check if this would violate the "8" in 8.3, ie. 9.2 */
669 if ((i >= 8) && (!HasExtension)) return TRUE;
670 }
671
672 /* The name *seems* to conform to 8.3 */
673 return FALSE;
674 }
675
676 BOOL
677 WINAPI
678 FindLFNorSFN_U(IN PWCHAR Path,
679 OUT PWCHAR *First,
680 OUT PWCHAR *Last,
681 IN BOOL UseShort)
682 {
683 PWCHAR p;
684 ULONG Length;
685 BOOL Found = 0;
686 ASSERT(Path);
687
688 /* Loop while there is something in the path */
689 while (TRUE)
690 {
691 /* Loop within the path skipping slashes */
692 while ((*Path == L'\\') || (*Path == L'/')) Path++;
693
694 /* Make sure there's something after the slashes too! */
695 if (*Path == UNICODE_NULL) break;
696
697 /* Now skip past the file name until we get to the first slash */
698 p = Path + 1;
699 while ((*p) && ((*p != L'\\') && (*p != L'/'))) p++;
700
701 /* Whatever is in between those two is now the file name length */
702 Length = p - Path;
703
704 /*
705 * Check if it is valid
706 * Note that !IsShortName != IsLongName, these two functions simply help
707 * us determine if a conversion is necessary or not.
708 * "Found" really means: "Is a conversion necessary?", hence the "!"
709 */
710 Found = UseShort ? !IsShortName_U(Path, Length) : !IsLongName_U(Path, Length);
711 if (Found)
712 {
713 /* It is! did the caller request to know the markers? */
714 if ((First) && (Last))
715 {
716 /* Return them */
717 *First = Path;
718 *Last = p;
719 }
720 break;
721 }
722
723 /* Is there anything else following this sub-path/filename? */
724 if (*p == UNICODE_NULL) break;
725
726 /* Yes, keep going */
727 Path = p + 1;
728 }
729
730 /* Return if anything was found and valid */
731 return Found;
732 }
733
734 PWCHAR
735 WINAPI
736 SkipPathTypeIndicator_U(IN LPWSTR Path)
737 {
738 PWCHAR ReturnPath;
739 ULONG i;
740
741 /* Check what kind of path this is and how many slashes to skip */
742 switch (RtlDetermineDosPathNameType_U(Path))
743 {
744 case RtlPathTypeDriveAbsolute:
745 return Path + 3;
746
747 case RtlPathTypeDriveRelative:
748 return Path + 2;
749
750 case RtlPathTypeRooted:
751 return Path + 1;
752
753 case RtlPathTypeRelative:
754 return Path;
755
756 case RtlPathTypeRootLocalDevice:
757 default:
758 return NULL;
759
760 case RtlPathTypeUncAbsolute:
761 case RtlPathTypeLocalDevice:
762
763 /* Keep going until we bypass the path indicators */
764 for (ReturnPath = Path + 2, i = 2; (i > 0) && (*ReturnPath); ReturnPath++)
765 {
766 /* We look for 2 slashes, so keep at it until we find them */
767 if ((*ReturnPath == L'\\') || (*ReturnPath == L'/')) i--;
768 }
769
770 return ReturnPath;
771 }
772 }
773
774 BOOL
775 WINAPI
776 BasepIsCurDirAllowedForPlainExeNames(VOID)
777 {
778 NTSTATUS Status;
779 UNICODE_STRING EmptyString;
780
781 RtlInitEmptyUnicodeString(&EmptyString, NULL, 0);
782 Status = RtlQueryEnvironmentVariable_U(NULL,
783 &NoDefaultCurrentDirectoryInExePath,
784 &EmptyString);
785 return !NT_SUCCESS(Status) && Status != STATUS_BUFFER_TOO_SMALL;
786 }
787
788 /* PUBLIC FUNCTIONS ***********************************************************/
789
790 /*
791 * @implemented
792 */
793 BOOL
794 WINAPI
795 SetDllDirectoryW(IN LPCWSTR lpPathName)
796 {
797 UNICODE_STRING OldDirectory, DllDirectory;
798
799 if (lpPathName)
800 {
801 if (wcschr(lpPathName, L';'))
802 {
803 SetLastError(ERROR_INVALID_PARAMETER);
804 return FALSE;
805 }
806 if (!RtlCreateUnicodeString(&DllDirectory, lpPathName))
807 {
808 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
809 return FALSE;
810 }
811 }
812 else
813 {
814 RtlInitUnicodeString(&DllDirectory, NULL);
815 }
816
817 RtlEnterCriticalSection(&BaseDllDirectoryLock);
818
819 OldDirectory = BaseDllDirectory;
820 BaseDllDirectory = DllDirectory;
821
822 RtlLeaveCriticalSection(&BaseDllDirectoryLock);
823
824 RtlFreeUnicodeString(&OldDirectory);
825 return TRUE;
826 }
827
828 /*
829 * @implemented
830 */
831 BOOL
832 WINAPI
833 SetDllDirectoryA(IN LPCSTR lpPathName)
834 {
835 ANSI_STRING AnsiDllDirectory;
836 UNICODE_STRING OldDirectory, DllDirectory;
837 NTSTATUS Status;
838
839 if (lpPathName)
840 {
841 if (strchr(lpPathName, ';'))
842 {
843 SetLastError(ERROR_INVALID_PARAMETER);
844 return FALSE;
845 }
846
847 Status = RtlInitAnsiStringEx(&AnsiDllDirectory, lpPathName);
848 if (NT_SUCCESS(Status))
849 {
850 Status = Basep8BitStringToUnicodeString(&DllDirectory,
851 &AnsiDllDirectory,
852 TRUE);
853 }
854
855 if (!NT_SUCCESS(Status))
856 {
857 BaseSetLastNTError(Status);
858 return FALSE;
859 }
860 }
861 else
862 {
863 RtlInitUnicodeString(&DllDirectory, NULL);
864 }
865
866 RtlEnterCriticalSection(&BaseDllDirectoryLock);
867
868 OldDirectory = BaseDllDirectory;
869 BaseDllDirectory = DllDirectory;
870
871 RtlLeaveCriticalSection(&BaseDllDirectoryLock);
872
873 RtlFreeUnicodeString(&OldDirectory);
874 return TRUE;
875 }
876
877 /*
878 * @implemented
879 */
880 DWORD
881 WINAPI
882 GetDllDirectoryW(IN DWORD nBufferLength,
883 OUT LPWSTR lpBuffer)
884 {
885 ULONG Length;
886
887 RtlEnterCriticalSection(&BaseDllDirectoryLock);
888
889 if ((nBufferLength * sizeof(WCHAR)) > BaseDllDirectory.Length)
890 {
891 RtlCopyMemory(lpBuffer, BaseDllDirectory.Buffer, BaseDllDirectory.Length);
892 Length = BaseDllDirectory.Length / sizeof(WCHAR);
893 lpBuffer[Length] = UNICODE_NULL;
894 }
895 else
896 {
897 Length = (BaseDllDirectory.Length + sizeof(UNICODE_NULL)) / sizeof(WCHAR);
898 if (lpBuffer) *lpBuffer = UNICODE_NULL;
899 }
900
901 RtlLeaveCriticalSection(&BaseDllDirectoryLock);
902 return Length;
903 }
904
905 /*
906 * @implemented
907 */
908 DWORD
909 WINAPI
910 GetDllDirectoryA(IN DWORD nBufferLength,
911 OUT LPSTR lpBuffer)
912 {
913 NTSTATUS Status;
914 ANSI_STRING AnsiDllDirectory;
915 ULONG Length;
916
917 RtlInitEmptyAnsiString(&AnsiDllDirectory, lpBuffer, nBufferLength);
918
919 RtlEnterCriticalSection(&BaseDllDirectoryLock);
920
921 Length = BasepUnicodeStringTo8BitSize(&BaseDllDirectory);
922 if (Length > nBufferLength)
923 {
924 Status = STATUS_SUCCESS;
925 if (lpBuffer) *lpBuffer = ANSI_NULL;
926 }
927 else
928 {
929 --Length;
930 Status = BasepUnicodeStringTo8BitString(&AnsiDllDirectory,
931 &BaseDllDirectory,
932 FALSE);
933 }
934
935 RtlLeaveCriticalSection(&BaseDllDirectoryLock);
936
937 if (!NT_SUCCESS(Status))
938 {
939 BaseSetLastNTError(Status);
940 Length = 0;
941 if (lpBuffer) *lpBuffer = ANSI_NULL;
942 }
943
944 return Length;
945 }
946
947 /*
948 * @implemented
949 */
950 BOOL
951 WINAPI
952 NeedCurrentDirectoryForExePathW(IN LPCWSTR ExeName)
953 {
954 if (wcschr(ExeName, L'\\')) return TRUE;
955
956 return BasepIsCurDirAllowedForPlainExeNames();
957 }
958
959 /*
960 * @implemented
961 */
962 BOOL
963 WINAPI
964 NeedCurrentDirectoryForExePathA(IN LPCSTR ExeName)
965 {
966 if (strchr(ExeName, '\\')) return TRUE;
967
968 return BasepIsCurDirAllowedForPlainExeNames();
969 }
970
971 /*
972 * @implemented
973 *
974 * NOTE: Many of these A functions may seem to do rather complex A<->W mapping
975 * beyond what you would usually expect. There are two main reasons:
976 *
977 * First, these APIs are subject to the ANSI/OEM File API selection status that
978 * the caller has chosen, so we must use the "8BitString" internal Base APIs.
979 *
980 * Secondly, the Wide APIs (coming from the 9x world) are coded to return the
981 * length of the paths in "ANSI" by dividing their internal Wide character count
982 * by two... this is usually correct when dealing with pure-ASCII codepages but
983 * not necessarily when dealing with MBCS pre-Unicode sets, which NT supports
984 * for CJK, for example.
985 */
986 DWORD
987 WINAPI
988 GetFullPathNameA(IN LPCSTR lpFileName,
989 IN DWORD nBufferLength,
990 IN LPSTR lpBuffer,
991 IN LPSTR *lpFilePart)
992 {
993 NTSTATUS Status;
994 PWCHAR Buffer;
995 ULONG PathSize, FilePartSize;
996 ANSI_STRING AnsiString;
997 UNICODE_STRING FileNameString, UniString;
998 PWCHAR LocalFilePart;
999 PWCHAR* FilePart;
1000
1001 /* If the caller wants filepart, use a local wide buffer since this is A */
1002 FilePart = lpFilePart != NULL ? &LocalFilePart : NULL;
1003
1004 /* Initialize for Quickie */
1005 FilePartSize = PathSize = 0;
1006 FileNameString.Buffer = NULL;
1007
1008 /* First get our string in Unicode */
1009 Status = Basep8BitStringToDynamicUnicodeString(&FileNameString, lpFileName);
1010 if (!NT_SUCCESS(Status)) goto Quickie;
1011
1012 /* Allocate a buffer to hold teh path name */
1013 Buffer = RtlAllocateHeap(RtlGetProcessHeap(),
1014 0,
1015 MAX_PATH * sizeof(WCHAR) + sizeof(UNICODE_NULL));
1016 if (!Buffer)
1017 {
1018 BaseSetLastNTError(STATUS_INSUFFICIENT_RESOURCES);
1019 goto Quickie;
1020 }
1021
1022 /* Call into RTL to get the full Unicode path name */
1023 PathSize = RtlGetFullPathName_U(FileNameString.Buffer,
1024 MAX_PATH * sizeof(WCHAR),
1025 Buffer,
1026 FilePart);
1027 if (PathSize <= (MAX_PATH * sizeof(WCHAR)))
1028 {
1029 /* The buffer will fit, get the real ANSI string size now */
1030 Status = RtlUnicodeToMultiByteSize(&PathSize, Buffer, PathSize);
1031 if (NT_SUCCESS(Status))
1032 {
1033 /* Now check if the user wanted file part size as well */
1034 if ((PathSize) && (lpFilePart) && (LocalFilePart))
1035 {
1036 /* Yep, so in this case get the length of the file part too */
1037 Status = RtlUnicodeToMultiByteSize(&FilePartSize,
1038 Buffer,
1039 (LocalFilePart - Buffer) *
1040 sizeof(WCHAR));
1041 if (!NT_SUCCESS(Status))
1042 {
1043 /* We failed to do that, so fail the whole call */
1044 BaseSetLastNTError(Status);
1045 PathSize = 0;
1046 }
1047 }
1048 }
1049 }
1050 else
1051 {
1052 /* Reset the path size since the buffer is not large enough */
1053 PathSize = 0;
1054 }
1055
1056 /* Either no path, or local buffer was too small, enter failure code */
1057 if (!PathSize) goto Quickie;
1058
1059 /* If the *caller's* buffer was too small, fail, but add in space for NULL */
1060 if (PathSize >= nBufferLength)
1061 {
1062 PathSize++;
1063 goto Quickie;
1064 }
1065
1066 /* So far so good, initialize a unicode string to convert back to ANSI/OEM */
1067 RtlInitUnicodeString(&UniString, Buffer);
1068 Status = BasepUnicodeStringTo8BitString(&AnsiString, &UniString, TRUE);
1069 if (!NT_SUCCESS(Status))
1070 {
1071 /* Final conversion failed, fail the call */
1072 BaseSetLastNTError(Status);
1073 PathSize = 0;
1074 }
1075 else
1076 {
1077 /* Conversion worked, now copy the ANSI/OEM buffer into the buffer */
1078 RtlCopyMemory(lpBuffer, AnsiString.Buffer, PathSize + 1);
1079 RtlFreeAnsiString(&AnsiString);
1080
1081 /* And finally, did the caller request file part information? */
1082 if (lpFilePart)
1083 {
1084 /* Use the size we computed earlier and add it to the buffer */
1085 *lpFilePart = LocalFilePart ? &lpBuffer[FilePartSize] : 0;
1086 }
1087 }
1088
1089 Quickie:
1090 /* Cleanup and return the path size */
1091 if (FileNameString.Buffer) RtlFreeUnicodeString(&FileNameString);
1092 if (Buffer) RtlFreeHeap(RtlGetProcessHeap(), 0, Buffer);
1093 return PathSize;
1094 }
1095
1096 /*
1097 * @implemented
1098 */
1099 DWORD
1100 WINAPI
1101 GetFullPathNameW(IN LPCWSTR lpFileName,
1102 IN DWORD nBufferLength,
1103 IN LPWSTR lpBuffer,
1104 OUT LPWSTR *lpFilePart)
1105 {
1106 /* Call Rtl to do the work */
1107 return RtlGetFullPathName_U((LPWSTR)lpFileName,
1108 nBufferLength * sizeof(WCHAR),
1109 lpBuffer,
1110 lpFilePart) / sizeof(WCHAR);
1111 }
1112
1113 /*
1114 * @implemented
1115 */
1116 DWORD
1117 WINAPI
1118 SearchPathA(IN LPCSTR lpPath,
1119 IN LPCSTR lpFileName,
1120 IN LPCSTR lpExtension,
1121 IN DWORD nBufferLength,
1122 IN LPSTR lpBuffer,
1123 OUT LPSTR *lpFilePart)
1124 {
1125 PUNICODE_STRING FileNameString;
1126 UNICODE_STRING PathString, ExtensionString;
1127 NTSTATUS Status;
1128 ULONG PathSize, FilePartSize, AnsiLength;
1129 PWCHAR LocalFilePart, Buffer;
1130 PWCHAR* FilePart;
1131
1132 /* If the caller wants filepart, use a local wide buffer since this is A */
1133 FilePart = lpFilePart != NULL ? &LocalFilePart : NULL;
1134
1135 /* Initialize stuff for Quickie */
1136 PathSize = 0;
1137 Buffer = NULL;
1138 ExtensionString.Buffer = PathString.Buffer = NULL;
1139
1140 /* Get the UNICODE_STRING file name */
1141 FileNameString = Basep8BitStringToStaticUnicodeString(lpFileName);
1142 if (!FileNameString) return 0;
1143
1144 /* Did the caller specify an extension */
1145 if (lpExtension)
1146 {
1147 /* Yup, convert it into UNICODE_STRING */
1148 Status = Basep8BitStringToDynamicUnicodeString(&ExtensionString,
1149 lpExtension);
1150 if (!NT_SUCCESS(Status)) goto Quickie;
1151 }
1152
1153 /* Did the caller specify a path */
1154 if (lpPath)
1155 {
1156 /* Yup, convert it into UNICODE_STRING */
1157 Status = Basep8BitStringToDynamicUnicodeString(&PathString, lpPath);
1158 if (!NT_SUCCESS(Status)) goto Quickie;
1159 }
1160
1161 /* Allocate our output buffer */
1162 Buffer = RtlAllocateHeap(RtlGetProcessHeap(), 0, nBufferLength * sizeof(WCHAR));
1163 if (!Buffer)
1164 {
1165 /* It failed, bail out */
1166 BaseSetLastNTError(STATUS_NO_MEMORY);
1167 goto Quickie;
1168 }
1169
1170 /* Now run the Wide search with the input buffer lengths */
1171 PathSize = SearchPathW(PathString.Buffer,
1172 FileNameString->Buffer,
1173 ExtensionString.Buffer,
1174 nBufferLength,
1175 Buffer,
1176 FilePart);
1177 if (PathSize <= nBufferLength)
1178 {
1179 /* It fits, but is it empty? If so, bail out */
1180 if (!PathSize) goto Quickie;
1181
1182 /* The length above is inexact, we need it in ANSI */
1183 Status = RtlUnicodeToMultiByteSize(&AnsiLength, Buffer, PathSize * sizeof(WCHAR));
1184 if (!NT_SUCCESS(Status))
1185 {
1186 /* Conversion failed, fail the call */
1187 PathSize = 0;
1188 BaseSetLastNTError(Status);
1189 goto Quickie;
1190 }
1191
1192 /* If the correct ANSI size is too big, return requird length plus a NULL */
1193 if (AnsiLength >= nBufferLength)
1194 {
1195 PathSize = AnsiLength + 1;
1196 goto Quickie;
1197 }
1198
1199 /* Now apply the final conversion to ANSI */
1200 Status = RtlUnicodeToMultiByteN(lpBuffer,
1201 nBufferLength - 1,
1202 &AnsiLength,
1203 Buffer,
1204 PathSize * sizeof(WCHAR));
1205 if (!NT_SUCCESS(Status))
1206 {
1207 /* Conversion failed, fail the whole call */
1208 PathSize = 0;
1209 BaseSetLastNTError(STATUS_NO_MEMORY);
1210 goto Quickie;
1211 }
1212
1213 /* NULL-terminate and return the real ANSI length */
1214 lpBuffer[AnsiLength] = ANSI_NULL;
1215 PathSize = AnsiLength;
1216
1217 /* Now check if the user wanted file part size as well */
1218 if (lpFilePart)
1219 {
1220 /* If we didn't get a file part, clear the caller's */
1221 if (!LocalFilePart)
1222 {
1223 *lpFilePart = NULL;
1224 }
1225 else
1226 {
1227 /* Yep, so in this case get the length of the file part too */
1228 Status = RtlUnicodeToMultiByteSize(&FilePartSize,
1229 Buffer,
1230 (LocalFilePart - Buffer) *
1231 sizeof(WCHAR));
1232 if (!NT_SUCCESS(Status))
1233 {
1234 /* We failed to do that, so fail the whole call */
1235 BaseSetLastNTError(Status);
1236 PathSize = 0;
1237 }
1238
1239 /* Return the file part buffer */
1240 *lpFilePart = lpBuffer + FilePartSize;
1241 }
1242 }
1243 }
1244 else
1245 {
1246 /* Our initial buffer guess was too small, allocate a bigger one */
1247 RtlFreeHeap(RtlGetProcessHeap(), 0, Buffer);
1248 Buffer = RtlAllocateHeap(RtlGetProcessHeap(), 0, PathSize * sizeof(WCHAR));
1249 if (!Buffer)
1250 {
1251 /* Out of memory, fail everything */
1252 BaseSetLastNTError(STATUS_NO_MEMORY);
1253 goto Quickie;
1254 }
1255
1256 /* Do the search again -- it will fail, we just want the path size */
1257 PathSize = SearchPathW(PathString.Buffer,
1258 FileNameString->Buffer,
1259 ExtensionString.Buffer,
1260 PathSize,
1261 Buffer,
1262 FilePart);
1263 if (!PathSize) goto Quickie;
1264
1265 /* Convert it to a correct size */
1266 Status = RtlUnicodeToMultiByteSize(&PathSize, Buffer, PathSize * sizeof(WCHAR));
1267 if (NT_SUCCESS(Status))
1268 {
1269 /* Make space for the NULL-char */
1270 PathSize++;
1271 }
1272 else
1273 {
1274 /* Conversion failed for some reason, fail the call */
1275 BaseSetLastNTError(Status);
1276 PathSize = 0;
1277 }
1278 }
1279
1280 Quickie:
1281 /* Cleanup/complete path */
1282 if (Buffer) RtlFreeHeap(RtlGetProcessHeap(), 0, Buffer);
1283 if (ExtensionString.Buffer) RtlFreeUnicodeString(&ExtensionString);
1284 if (PathString.Buffer) RtlFreeUnicodeString(&PathString);
1285 return PathSize;
1286 }
1287
1288 /***********************************************************************
1289 * ContainsPath (Wine name: contains_pathW)
1290 *
1291 * Check if the file name contains a path; helper for SearchPathW.
1292 * A relative path is not considered a path unless it starts with ./ or ../
1293 */
1294 static
1295 BOOL
1296 ContainsPath(LPCWSTR name)
1297 {
1298 if (RtlDetermineDosPathNameType_U(name) != RtlPathTypeRelative) return TRUE;
1299 if (name[0] != '.') return FALSE;
1300 if (name[1] == '/' || name[1] == '\\' || name[1] == '\0') return TRUE;
1301 return (name[1] == '.' && (name[2] == '/' || name[2] == '\\'));
1302 }
1303
1304 /**
1305 * @name GetDllLoadPath
1306 *
1307 * Internal function to compute the load path to use for a given dll.
1308 *
1309 * @remarks Returned pointer must be freed by caller.
1310 */
1311
1312 LPWSTR
1313 GetDllLoadPath(LPCWSTR lpModule)
1314 {
1315 ULONG Pos = 0, Length = 4, Tmp;
1316 PWCHAR EnvironmentBufferW = NULL;
1317 LPCWSTR lpModuleEnd = NULL;
1318 UNICODE_STRING ModuleName;
1319 DWORD LastError = GetLastError(); /* GetEnvironmentVariable changes LastError */
1320
1321 // FIXME: This function is used only by SearchPathW, and is deprecated and will be deleted ASAP.
1322
1323 if (lpModule != NULL && wcslen(lpModule) > 2 && lpModule[1] == ':')
1324 {
1325 lpModuleEnd = lpModule + wcslen(lpModule);
1326 }
1327 else
1328 {
1329 ModuleName = NtCurrentPeb()->ProcessParameters->ImagePathName;
1330 lpModule = ModuleName.Buffer;
1331 lpModuleEnd = lpModule + (ModuleName.Length / sizeof(WCHAR));
1332 }
1333
1334 if (lpModule != NULL)
1335 {
1336 while (lpModuleEnd > lpModule && *lpModuleEnd != L'/' &&
1337 *lpModuleEnd != L'\\' && *lpModuleEnd != L':')
1338 {
1339 --lpModuleEnd;
1340 }
1341 Length = (lpModuleEnd - lpModule) + 1;
1342 }
1343
1344 Length += GetCurrentDirectoryW(0, NULL);
1345 Length += GetDllDirectoryW(0, NULL);
1346 Length += GetSystemDirectoryW(NULL, 0);
1347 Length += GetWindowsDirectoryW(NULL, 0);
1348 Length += GetEnvironmentVariableW(L"PATH", NULL, 0);
1349
1350 EnvironmentBufferW = RtlAllocateHeap(RtlGetProcessHeap(), 0,
1351 (Length + 1) * sizeof(WCHAR));
1352 if (EnvironmentBufferW == NULL)
1353 {
1354 return NULL;
1355 }
1356
1357 if (lpModule)
1358 {
1359 RtlCopyMemory(EnvironmentBufferW, lpModule,
1360 (lpModuleEnd - lpModule) * sizeof(WCHAR));
1361 Pos += lpModuleEnd - lpModule;
1362 EnvironmentBufferW[Pos++] = L';';
1363 }
1364
1365 Tmp = GetCurrentDirectoryW(Length, EnvironmentBufferW + Pos);
1366 if(Tmp > 0 && Tmp < Length - Pos)
1367 {
1368 Pos += Tmp;
1369 if(Pos < Length) EnvironmentBufferW[Pos++] = L';';
1370 }
1371
1372 Tmp = GetDllDirectoryW(Length - Pos, EnvironmentBufferW + Pos);
1373 if(Tmp > 0 && Tmp < Length - Pos)
1374 {
1375 Pos += Tmp;
1376 if(Pos < Length) EnvironmentBufferW[Pos++] = L';';
1377 }
1378
1379 Tmp = GetSystemDirectoryW(EnvironmentBufferW + Pos, Length - Pos);
1380 if(Tmp > 0 && Tmp < Length - Pos)
1381 {
1382 Pos += Tmp;
1383 if(Pos < Length) EnvironmentBufferW[Pos++] = L';';
1384 }
1385
1386 Tmp = GetWindowsDirectoryW(EnvironmentBufferW + Pos, Length - Pos);
1387 if(Tmp > 0 && Tmp < Length - Pos)
1388 {
1389 Pos += Tmp;
1390 if(Pos < Length) EnvironmentBufferW[Pos++] = L';';
1391 }
1392
1393 Tmp = GetEnvironmentVariableW(L"PATH", EnvironmentBufferW + Pos, Length - Pos);
1394
1395 /* Make sure buffer is null terminated */
1396 EnvironmentBufferW[Pos++] = UNICODE_NULL;
1397
1398
1399 SetLastError(LastError);
1400 return EnvironmentBufferW;
1401 }
1402
1403 /*
1404 * @implemented
1405 */
1406 DWORD
1407 WINAPI
1408 SearchPathW(LPCWSTR lpPath,
1409 LPCWSTR lpFileName,
1410 LPCWSTR lpExtension,
1411 DWORD nBufferLength,
1412 LPWSTR lpBuffer,
1413 LPWSTR *lpFilePart)
1414 {
1415 DWORD ret = 0;
1416
1417 if (!lpFileName || !lpFileName[0])
1418 {
1419 SetLastError(ERROR_INVALID_PARAMETER);
1420 return 0;
1421 }
1422
1423 /* If the name contains an explicit path, ignore the path */
1424 if (ContainsPath(lpFileName))
1425 {
1426 /* try first without extension */
1427 if (RtlDoesFileExists_U(lpFileName))
1428 return GetFullPathNameW(lpFileName, nBufferLength, lpBuffer, lpFilePart);
1429
1430 if (lpExtension)
1431 {
1432 LPCWSTR p = wcsrchr(lpFileName, '.');
1433 if (p && !strchr((const char *)p, '/') && !wcschr( p, '\\' ))
1434 lpExtension = NULL; /* Ignore the specified extension */
1435 }
1436
1437 /* Allocate a buffer for the file name and extension */
1438 if (lpExtension)
1439 {
1440 LPWSTR tmp;
1441 DWORD len = wcslen(lpFileName) + wcslen(lpExtension);
1442
1443 if (!(tmp = RtlAllocateHeap(RtlGetProcessHeap(), 0, (len + 1) * sizeof(WCHAR))))
1444 {
1445 SetLastError(ERROR_OUTOFMEMORY);
1446 return 0;
1447 }
1448 wcscpy(tmp, lpFileName);
1449 wcscat(tmp, lpExtension);
1450 if (RtlDoesFileExists_U(tmp))
1451 ret = GetFullPathNameW(tmp, nBufferLength, lpBuffer, lpFilePart);
1452 RtlFreeHeap(RtlGetProcessHeap(), 0, tmp);
1453 }
1454 }
1455 else if (lpPath && lpPath[0]) /* search in the specified path */
1456 {
1457 ret = RtlDosSearchPath_U(lpPath,
1458 lpFileName,
1459 lpExtension,
1460 nBufferLength * sizeof(WCHAR),
1461 lpBuffer,
1462 lpFilePart) / sizeof(WCHAR);
1463 }
1464 else /* search in the default path */
1465 {
1466 WCHAR *DllPath = GetDllLoadPath(NULL);
1467
1468 if (DllPath)
1469 {
1470 ret = RtlDosSearchPath_U(DllPath,
1471 lpFileName,
1472 lpExtension,
1473 nBufferLength * sizeof(WCHAR),
1474 lpBuffer,
1475 lpFilePart) / sizeof(WCHAR);
1476 RtlFreeHeap(RtlGetProcessHeap(), 0, DllPath);
1477 }
1478 else
1479 {
1480 SetLastError(ERROR_OUTOFMEMORY);
1481 return 0;
1482 }
1483 }
1484
1485 if (!ret) SetLastError(ERROR_FILE_NOT_FOUND);
1486
1487 return ret;
1488 }
1489
1490 /*
1491 * @implemented
1492 */
1493 DWORD
1494 WINAPI
1495 GetLongPathNameW(IN LPCWSTR lpszShortPath,
1496 IN LPWSTR lpszLongPath,
1497 IN DWORD cchBuffer)
1498 {
1499 PWCHAR Path, Original, First, Last, Buffer, Src, Dst;
1500 ULONG Length;
1501 WCHAR LastChar;
1502 HANDLE FindHandle;
1503 DWORD ReturnLength;
1504 ULONG ErrorMode;
1505 BOOLEAN Found = FALSE;
1506 WIN32_FIND_DATAW FindFileData;
1507
1508 /* Initialize so Quickie knows there's nothing to do */
1509 Buffer = Original = NULL;
1510 ReturnLength = 0;
1511
1512 /* First check if the input path was obviously NULL */
1513 if (!lpszShortPath)
1514 {
1515 /* Fail the request */
1516 SetLastError(ERROR_INVALID_PARAMETER);
1517 return 0;
1518 }
1519
1520 /* We will be touching removed, removable drives -- don't warn the user */
1521 ErrorMode = SetErrorMode(SEM_NOOPENFILEERRORBOX | SEM_FAILCRITICALERRORS);
1522
1523 /* Do a simple check to see if the path exists */
1524 if (GetFileAttributesW(lpszShortPath) == INVALID_FILE_ATTRIBUTES)
1525 {
1526 /* It doesn't, so fail */
1527 ReturnLength = 0;
1528 goto Quickie;
1529 }
1530
1531 /* Now get a pointer to the actual path, skipping indicators */
1532 Path = SkipPathTypeIndicator_U((LPWSTR)lpszShortPath);
1533
1534 /* Is there any path or filename in there? */
1535 if (!(Path) ||
1536 (*Path == UNICODE_NULL) ||
1537 !(FindLFNorSFN_U(Path, &First, &Last, FALSE)))
1538 {
1539 /* There isn't, so the long path is simply the short path */
1540 ReturnLength = wcslen(lpszShortPath);
1541
1542 /* Is there space for it? */
1543 if ((cchBuffer > ReturnLength) && (lpszLongPath))
1544 {
1545 /* Make sure the pointers aren't already the same */
1546 if (lpszLongPath != lpszShortPath)
1547 {
1548 /* They're not -- copy the short path into the long path */
1549 RtlMoveMemory(lpszLongPath,
1550 lpszShortPath,
1551 ReturnLength * sizeof(WCHAR) + sizeof(UNICODE_NULL));
1552 }
1553 }
1554 else
1555 {
1556 /* Otherwise, let caller know we need a bigger buffer, include NULL */
1557 ReturnLength++;
1558 }
1559 goto Quickie;
1560 }
1561
1562 /* We are still in the game -- compute the current size */
1563 Length = wcslen(lpszShortPath) + sizeof(ANSI_NULL);
1564 Original = RtlAllocateHeap(RtlGetProcessHeap(), 0, Length * sizeof(WCHAR));
1565 if (!Original) goto ErrorQuickie;
1566
1567 /* Make a copy of it */
1568 RtlMoveMemory(Original, lpszShortPath, Length * sizeof(WCHAR));
1569
1570 /* Compute the new first and last markers */
1571 First = &Original[First - lpszShortPath];
1572 Last = &Original[Last - lpszShortPath];
1573
1574 /* Set the current destination pointer for a copy */
1575 Dst = lpszLongPath;
1576
1577 /*
1578 * Windows allows the paths to overlap -- we have to be careful with this and
1579 * see if it's same to do so, and if not, allocate our own internal buffer
1580 * that we'll return at the end.
1581 *
1582 * This is also why we use RtlMoveMemory everywhere. Don't use RtlCopyMemory!
1583 */
1584 if ((cchBuffer) && (lpszLongPath) &&
1585 (((lpszLongPath >= lpszShortPath) && (lpszLongPath < &lpszShortPath[Length])) ||
1586 ((lpszLongPath < lpszShortPath) && (&lpszLongPath[cchBuffer] >= lpszShortPath))))
1587 {
1588 Buffer = RtlAllocateHeap(RtlGetProcessHeap(), 0, cchBuffer * sizeof(WCHAR));
1589 if (!Buffer) goto ErrorQuickie;
1590
1591 /* New destination */
1592 Dst = Buffer;
1593 }
1594
1595 /* Prepare for the loop */
1596 Src = Original;
1597 ReturnLength = 0;
1598 while (TRUE)
1599 {
1600 /* Current delta in the loop */
1601 Length = First - Src;
1602
1603 /* Update the return length by it */
1604 ReturnLength += Length;
1605
1606 /* Is there a delta? If so, is there space and buffer for it? */
1607 if ((Length) && (cchBuffer > ReturnLength) && (lpszLongPath))
1608 {
1609 RtlMoveMemory(Dst, Src, Length * sizeof(WCHAR));
1610 Dst += Length;
1611 }
1612
1613 /* "Terminate" this portion of the path's substring so we can do a find */
1614 LastChar = *Last;
1615 *Last = UNICODE_NULL;
1616 FindHandle = FindFirstFileW(Original, &FindFileData);
1617 *Last = LastChar;
1618
1619 /* This portion wasn't found, so fail */
1620 if (FindHandle == INVALID_HANDLE_VALUE)
1621 {
1622 ReturnLength = 0;
1623 break;
1624 }
1625
1626 /* Close the find handle */
1627 FindClose(FindHandle);
1628
1629 /* Now check the length of the long name */
1630 Length = wcslen(FindFileData.cFileName);
1631 if (Length)
1632 {
1633 /* This is our new first marker */
1634 First = FindFileData.cFileName;
1635 }
1636 else
1637 {
1638 /* Otherwise, the name is the delta between our current markers */
1639 Length = Last - First;
1640 }
1641
1642 /* Update the return length with the short name length, if any */
1643 ReturnLength += Length;
1644
1645 /* Once again check for appropriate space and buffer */
1646 if ((cchBuffer > ReturnLength) && (lpszLongPath))
1647 {
1648 /* And do the copy if there is */
1649 RtlMoveMemory(Dst, First, Length * sizeof(WCHAR));
1650 Dst += Length;
1651 }
1652
1653 /* Now update the source pointer */
1654 Src = Last;
1655 if (*Src == UNICODE_NULL) break;
1656
1657 /* Are there more names in there? */
1658 Found = FindLFNorSFN_U(Src, &First, &Last, FALSE);
1659 if (!Found) break;
1660 }
1661
1662 /* The loop is done, is there anything left? */
1663 if (ReturnLength)
1664 {
1665 /* Get the length of the straggling path */
1666 Length = wcslen(Src);
1667 ReturnLength += Length;
1668
1669 /* Once again check for appropriate space and buffer */
1670 if ((cchBuffer > ReturnLength) && (lpszLongPath))
1671 {
1672 /* And do the copy if there is -- accounting for NULL here */
1673 RtlMoveMemory(Dst, Src, Length * sizeof(WCHAR) + sizeof(UNICODE_NULL));
1674
1675 /* What about our buffer? */
1676 if (Buffer)
1677 {
1678 /* Copy it into the caller's long path */
1679 RtlMoveMemory(lpszLongPath,
1680 Buffer,
1681 ReturnLength * sizeof(WCHAR) + sizeof(UNICODE_NULL));
1682 }
1683 }
1684 else
1685 {
1686 /* Buffer is too small, let the caller know, making space for NULL */
1687 ReturnLength++;
1688 }
1689 }
1690
1691 /* We're all done */
1692 goto Quickie;
1693
1694 ErrorQuickie:
1695 /* This is the goto for memory failures */
1696 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
1697
1698 Quickie:
1699 /* General function end: free memory, restore error mode, return length */
1700 if (Original) RtlFreeHeap(RtlGetProcessHeap(), 0, Original);
1701 if (Buffer) RtlFreeHeap(RtlGetProcessHeap(), 0, Buffer);
1702 SetErrorMode(ErrorMode);
1703 return ReturnLength;
1704 }
1705
1706 /*
1707 * @implemented
1708 */
1709 DWORD
1710 WINAPI
1711 GetLongPathNameA(IN LPCSTR lpszShortPath,
1712 IN LPSTR lpszLongPath,
1713 IN DWORD cchBuffer)
1714 {
1715 ULONG Result, PathLength;
1716 PWCHAR LongPath;
1717 NTSTATUS Status;
1718 UNICODE_STRING LongPathUni, ShortPathUni;
1719 ANSI_STRING LongPathAnsi;
1720 WCHAR LongPathBuffer[MAX_PATH];
1721
1722 LongPath = NULL;
1723 LongPathAnsi.Buffer = NULL;
1724 ShortPathUni.Buffer = NULL;
1725 Result = 0;
1726
1727 if (!lpszShortPath)
1728 {
1729 SetLastError(ERROR_INVALID_PARAMETER);
1730 return 0;
1731 }
1732
1733 Status = Basep8BitStringToDynamicUnicodeString(&ShortPathUni, lpszShortPath);
1734 if (!NT_SUCCESS(Status)) goto Quickie;
1735
1736 LongPath = LongPathBuffer;
1737
1738 PathLength = GetLongPathNameW(ShortPathUni.Buffer, LongPathBuffer, MAX_PATH);
1739 if (PathLength >= MAX_PATH)
1740 {
1741 LongPath = RtlAllocateHeap(RtlGetProcessHeap(), 0, PathLength * sizeof(WCHAR));
1742 if (!LongPath)
1743 {
1744 PathLength = 0;
1745 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
1746 }
1747 else
1748 {
1749 PathLength = GetLongPathNameW(ShortPathUni.Buffer, LongPath, PathLength);
1750 }
1751 }
1752
1753 if (!PathLength) goto Quickie;
1754
1755 ShortPathUni.MaximumLength = PathLength * sizeof(WCHAR) + sizeof(UNICODE_NULL);
1756 LongPathUni.Buffer = LongPath;
1757 LongPathUni.Length = PathLength * sizeof(WCHAR);
1758
1759 Status = BasepUnicodeStringTo8BitString(&LongPathAnsi, &LongPathUni, TRUE);
1760 if (!NT_SUCCESS(Status))
1761 {
1762 BaseSetLastNTError(Status);
1763 Result = 0;
1764 }
1765
1766 Result = LongPathAnsi.Length;
1767 if ((lpszLongPath) && (cchBuffer > LongPathAnsi.Length))
1768 {
1769 RtlMoveMemory(lpszLongPath, LongPathAnsi.Buffer, LongPathAnsi.Length);
1770 lpszLongPath[Result] = ANSI_NULL;
1771 }
1772 else
1773 {
1774 Result = LongPathAnsi.Length + sizeof(ANSI_NULL);
1775 }
1776
1777 Quickie:
1778 if (ShortPathUni.Buffer) RtlFreeUnicodeString(&ShortPathUni);
1779 if (LongPathAnsi.Buffer) RtlFreeAnsiString(&LongPathAnsi);
1780 if ((LongPath) && (LongPath != LongPathBuffer))
1781 {
1782 RtlFreeHeap(RtlGetProcessHeap(), 0, LongPath);
1783 }
1784 return Result;
1785 }
1786
1787 /*
1788 * @implemented
1789 */
1790 DWORD
1791 WINAPI
1792 GetShortPathNameA(IN LPCSTR lpszLongPath,
1793 IN LPSTR lpszShortPath,
1794 IN DWORD cchBuffer)
1795 {
1796 ULONG Result, PathLength;
1797 PWCHAR ShortPath;
1798 NTSTATUS Status;
1799 UNICODE_STRING LongPathUni, ShortPathUni;
1800 ANSI_STRING ShortPathAnsi;
1801 WCHAR ShortPathBuffer[MAX_PATH];
1802
1803 ShortPath = NULL;
1804 ShortPathAnsi.Buffer = NULL;
1805 LongPathUni.Buffer = NULL;
1806 Result = 0;
1807
1808 if (!lpszLongPath)
1809 {
1810 SetLastError(ERROR_INVALID_PARAMETER);
1811 return 0;
1812 }
1813
1814 Status = Basep8BitStringToDynamicUnicodeString(&LongPathUni, lpszLongPath);
1815 if (!NT_SUCCESS(Status)) goto Quickie;
1816
1817 ShortPath = ShortPathBuffer;
1818
1819 PathLength = GetShortPathNameW(LongPathUni.Buffer, ShortPathBuffer, MAX_PATH);
1820 if (PathLength >= MAX_PATH)
1821 {
1822 ShortPath = RtlAllocateHeap(RtlGetProcessHeap(), 0, PathLength * sizeof(WCHAR));
1823 if (!ShortPath)
1824 {
1825 PathLength = 0;
1826 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
1827 }
1828 else
1829 {
1830 PathLength = GetShortPathNameW(LongPathUni.Buffer, ShortPath, PathLength);
1831 }
1832 }
1833
1834 if (!PathLength) goto Quickie;
1835
1836 LongPathUni.MaximumLength = PathLength * sizeof(WCHAR) + sizeof(UNICODE_NULL);
1837 ShortPathUni.Buffer = ShortPath;
1838 ShortPathUni.Length = PathLength * sizeof(WCHAR);
1839
1840 Status = BasepUnicodeStringTo8BitString(&ShortPathAnsi, &ShortPathUni, TRUE);
1841 if (!NT_SUCCESS(Status))
1842 {
1843 BaseSetLastNTError(Status);
1844 Result = 0;
1845 }
1846
1847 Result = ShortPathAnsi.Length;
1848 if ((lpszShortPath) && (cchBuffer > ShortPathAnsi.Length))
1849 {
1850 RtlMoveMemory(lpszShortPath, ShortPathAnsi.Buffer, ShortPathAnsi.Length);
1851 lpszShortPath[Result] = ANSI_NULL;
1852 }
1853 else
1854 {
1855 Result = ShortPathAnsi.Length + sizeof(ANSI_NULL);
1856 }
1857
1858 Quickie:
1859 if (LongPathUni.Buffer) RtlFreeUnicodeString(&LongPathUni);
1860 if (ShortPathAnsi.Buffer) RtlFreeAnsiString(&ShortPathAnsi);
1861 if ((ShortPath) && (ShortPath != ShortPathBuffer))
1862 {
1863 RtlFreeHeap(RtlGetProcessHeap(), 0, ShortPath);
1864 }
1865 return Result;
1866 }
1867
1868 /*
1869 * @implemented
1870 */
1871 DWORD
1872 WINAPI
1873 GetShortPathNameW(IN LPCWSTR lpszLongPath,
1874 IN LPWSTR lpszShortPath,
1875 IN DWORD cchBuffer)
1876 {
1877 PWCHAR Path, Original, First, Last, Buffer, Src, Dst;
1878 ULONG Length;
1879 WCHAR LastChar;
1880 HANDLE FindHandle;
1881 DWORD ReturnLength;
1882 ULONG ErrorMode;
1883 BOOLEAN Found = FALSE;
1884 WIN32_FIND_DATAW FindFileData;
1885
1886 /* Initialize so Quickie knows there's nothing to do */
1887 Buffer = Original = NULL;
1888 ReturnLength = 0;
1889
1890 /* First check if the input path was obviously NULL */
1891 if (!lpszLongPath)
1892 {
1893 /* Fail the request */
1894 SetLastError(ERROR_INVALID_PARAMETER);
1895 return 0;
1896 }
1897
1898 /* We will be touching removed, removable drives -- don't warn the user */
1899 ErrorMode = SetErrorMode(SEM_NOOPENFILEERRORBOX | SEM_FAILCRITICALERRORS);
1900
1901 /* Do a simple check to see if the path exists */
1902 if (GetFileAttributesW(lpszLongPath) == INVALID_FILE_ATTRIBUTES)
1903 {
1904 /* Windows checks for an application compatibility flag to allow this */
1905 if (!(NtCurrentPeb()) || !(NtCurrentPeb()->AppCompatFlags.LowPart & 1))
1906 {
1907 /* It doesn't, so fail */
1908 ReturnLength = 0;
1909 goto Quickie;
1910 }
1911 }
1912
1913 /* Now get a pointer to the actual path, skipping indicators */
1914 Path = SkipPathTypeIndicator_U((LPWSTR)lpszLongPath);
1915
1916 /* Is there any path or filename in there? */
1917 if (!(Path) ||
1918 (*Path == UNICODE_NULL) ||
1919 !(FindLFNorSFN_U(Path, &First, &Last, TRUE)))
1920 {
1921 /* There isn't, so the long path is simply the short path */
1922 ReturnLength = wcslen(lpszLongPath);
1923
1924 /* Is there space for it? */
1925 if ((cchBuffer > ReturnLength) && (lpszShortPath))
1926 {
1927 /* Make sure the pointers aren't already the same */
1928 if (lpszLongPath != lpszShortPath)
1929 {
1930 /* They're not -- copy the short path into the long path */
1931 RtlMoveMemory(lpszShortPath,
1932 lpszLongPath,
1933 ReturnLength * sizeof(WCHAR) + sizeof(UNICODE_NULL));
1934 }
1935 }
1936 else
1937 {
1938 /* Otherwise, let caller know we need a bigger buffer, include NULL */
1939 ReturnLength++;
1940 }
1941 goto Quickie;
1942 }
1943
1944 /* We are still in the game -- compute the current size */
1945 Length = wcslen(lpszLongPath) + sizeof(ANSI_NULL);
1946 Original = RtlAllocateHeap(RtlGetProcessHeap(), 0, Length * sizeof(WCHAR));
1947 if (!Original) goto ErrorQuickie;
1948
1949 /* Make a copy of it */
1950 wcsncpy(Original, lpszLongPath, Length);
1951
1952 /* Compute the new first and last markers */
1953 First = &Original[First - lpszLongPath];
1954 Last = &Original[Last - lpszLongPath];
1955
1956 /* Set the current destination pointer for a copy */
1957 Dst = lpszShortPath;
1958
1959 /*
1960 * Windows allows the paths to overlap -- we have to be careful with this and
1961 * see if it's same to do so, and if not, allocate our own internal buffer
1962 * that we'll return at the end.
1963 *
1964 * This is also why we use RtlMoveMemory everywhere. Don't use RtlCopyMemory!
1965 */
1966 if ((cchBuffer) && (lpszShortPath) &&
1967 (((lpszShortPath >= lpszLongPath) && (lpszShortPath < &lpszLongPath[Length])) ||
1968 ((lpszShortPath < lpszLongPath) && (&lpszShortPath[cchBuffer] >= lpszLongPath))))
1969 {
1970 Buffer = RtlAllocateHeap(RtlGetProcessHeap(), 0, cchBuffer * sizeof(WCHAR));
1971 if (!Buffer) goto ErrorQuickie;
1972
1973 /* New destination */
1974 Dst = Buffer;
1975 }
1976
1977 /* Prepare for the loop */
1978 Src = Original;
1979 ReturnLength = 0;
1980 while (TRUE)
1981 {
1982 /* Current delta in the loop */
1983 Length = First - Src;
1984
1985 /* Update the return length by it */
1986 ReturnLength += Length;
1987
1988 /* Is there a delta? If so, is there space and buffer for it? */
1989 if ((Length) && (cchBuffer > ReturnLength) && (lpszShortPath))
1990 {
1991 RtlMoveMemory(Dst, Src, Length * sizeof(WCHAR));
1992 Dst += Length;
1993 }
1994
1995 /* "Terminate" this portion of the path's substring so we can do a find */
1996 LastChar = *Last;
1997 *Last = UNICODE_NULL;
1998 FindHandle = FindFirstFileW(Original, &FindFileData);
1999 *Last = LastChar;
2000
2001 /* This portion wasn't found, so fail */
2002 if (FindHandle == INVALID_HANDLE_VALUE)
2003 {
2004 ReturnLength = 0;
2005 break;
2006 }
2007
2008 /* Close the find handle */
2009 FindClose(FindHandle);
2010
2011 /* Now check the length of the short name */
2012 Length = wcslen(FindFileData.cAlternateFileName);
2013 if (Length)
2014 {
2015 /* This is our new first marker */
2016 First = FindFileData.cAlternateFileName;
2017 }
2018 else
2019 {
2020 /* Otherwise, the name is the delta between our current markers */
2021 Length = Last - First;
2022 }
2023
2024 /* Update the return length with the short name length, if any */
2025 ReturnLength += Length;
2026
2027 /* Once again check for appropriate space and buffer */
2028 if ((cchBuffer > ReturnLength) && (lpszShortPath))
2029 {
2030 /* And do the copy if there is */
2031 RtlMoveMemory(Dst, First, Length * sizeof(WCHAR));
2032 Dst += Length;
2033 }
2034
2035 /* Now update the source pointer */
2036 Src = Last;
2037 if (*Src == UNICODE_NULL) break;
2038
2039 /* Are there more names in there? */
2040 Found = FindLFNorSFN_U(Src, &First, &Last, TRUE);
2041 if (!Found) break;
2042 }
2043
2044 /* The loop is done, is there anything left? */
2045 if (ReturnLength)
2046 {
2047 /* Get the length of the straggling path */
2048 Length = wcslen(Src);
2049 ReturnLength += Length;
2050
2051 /* Once again check for appropriate space and buffer */
2052 if ((cchBuffer > ReturnLength) && (lpszShortPath))
2053 {
2054 /* And do the copy if there is -- accounting for NULL here */
2055 RtlMoveMemory(Dst, Src, Length * sizeof(WCHAR) + sizeof(UNICODE_NULL));
2056
2057 /* What about our buffer? */
2058 if (Buffer)
2059 {
2060 /* Copy it into the caller's long path */
2061 RtlMoveMemory(lpszShortPath,
2062 Buffer,
2063 ReturnLength * sizeof(WCHAR) + sizeof(UNICODE_NULL));
2064 }
2065 }
2066 else
2067 {
2068 /* Buffer is too small, let the caller know, making space for NULL */
2069 ReturnLength++;
2070 }
2071 }
2072
2073 /* We're all done */
2074 goto Quickie;
2075
2076 ErrorQuickie:
2077 /* This is the goto for memory failures */
2078 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
2079
2080 Quickie:
2081 /* General function end: free memory, restore error mode, return length */
2082 if (Original) RtlFreeHeap(RtlGetProcessHeap(), 0, Original);
2083 if (Buffer) RtlFreeHeap(RtlGetProcessHeap(), 0, Buffer);
2084 SetErrorMode(ErrorMode);
2085 return ReturnLength;
2086 }
2087
2088 /*
2089 * @implemented
2090 *
2091 * NOTE: Windows returns a dos/short (8.3) path
2092 */
2093 DWORD
2094 WINAPI
2095 GetTempPathA(IN DWORD nBufferLength,
2096 IN LPSTR lpBuffer)
2097 {
2098 WCHAR BufferW[MAX_PATH];
2099 DWORD ret;
2100
2101 ret = GetTempPathW(MAX_PATH, BufferW);
2102
2103 if (!ret) return 0;
2104
2105 if (ret > MAX_PATH)
2106 {
2107 SetLastError(ERROR_FILENAME_EXCED_RANGE);
2108 return 0;
2109 }
2110
2111 return FilenameW2A_FitOrFail(lpBuffer, nBufferLength, BufferW, ret+1);
2112 }
2113
2114 /*
2115 * @implemented
2116 *
2117 * ripped from wine
2118 */
2119 DWORD
2120 WINAPI
2121 GetTempPathW(IN DWORD count,
2122 IN LPWSTR path)
2123 {
2124 static const WCHAR tmp[] = { 'T', 'M', 'P', 0 };
2125 static const WCHAR temp[] = { 'T', 'E', 'M', 'P', 0 };
2126 static const WCHAR userprofile[] = { 'U','S','E','R','P','R','O','F','I','L','E',0 };
2127 WCHAR tmp_path[MAX_PATH];
2128 UINT ret;
2129
2130 DPRINT("%u,%p\n", count, path);
2131
2132 if (!(ret = GetEnvironmentVariableW( tmp, tmp_path, MAX_PATH )) &&
2133 !(ret = GetEnvironmentVariableW( temp, tmp_path, MAX_PATH )) &&
2134 !(ret = GetEnvironmentVariableW( userprofile, tmp_path, MAX_PATH )) &&
2135 !(ret = GetWindowsDirectoryW( tmp_path, MAX_PATH )))
2136 return 0;
2137
2138 if (ret > MAX_PATH)
2139 {
2140 SetLastError(ERROR_FILENAME_EXCED_RANGE);
2141 return 0;
2142 }
2143
2144 ret = GetFullPathNameW(tmp_path, MAX_PATH, tmp_path, NULL);
2145 if (!ret) return 0;
2146
2147 if (ret > MAX_PATH - 2)
2148 {
2149 SetLastError(ERROR_FILENAME_EXCED_RANGE);
2150 return 0;
2151 }
2152
2153 if (tmp_path[ret-1] != '\\')
2154 {
2155 tmp_path[ret++] = '\\';
2156 tmp_path[ret] = '\0';
2157 }
2158
2159 ret++; /* add space for terminating 0 */
2160
2161 if (count)
2162 {
2163 lstrcpynW(path, tmp_path, count);
2164 if (count >= ret)
2165 ret--; /* return length without 0 */
2166 else if (count < 4)
2167 path[0] = 0; /* avoid returning ambiguous "X:" */
2168 }
2169
2170 DPRINT("GetTempPathW returning %u, %S\n", ret, path);
2171 return ret;
2172 }
2173
2174 /*
2175 * @implemented
2176 */
2177 DWORD
2178 WINAPI
2179 GetCurrentDirectoryA(IN DWORD nBufferLength,
2180 IN LPSTR lpBuffer)
2181 {
2182 ANSI_STRING AnsiString;
2183 NTSTATUS Status;
2184 PUNICODE_STRING StaticString;
2185 ULONG MaxLength;
2186
2187 StaticString = &NtCurrentTeb()->StaticUnicodeString;
2188
2189 MaxLength = nBufferLength;
2190 if (nBufferLength >= UNICODE_STRING_MAX_BYTES)
2191 {
2192 MaxLength = UNICODE_STRING_MAX_BYTES - 1;
2193 }
2194
2195 StaticString->Length = RtlGetCurrentDirectory_U(StaticString->MaximumLength,
2196 StaticString->Buffer);
2197 Status = RtlUnicodeToMultiByteSize(&nBufferLength,
2198 StaticString->Buffer,
2199 StaticString->Length);
2200 if (!NT_SUCCESS(Status))
2201 {
2202 BaseSetLastNTError(Status);
2203 return 0;
2204 }
2205
2206 if (MaxLength <= nBufferLength)
2207 {
2208 return nBufferLength + 1;
2209 }
2210
2211 AnsiString.Buffer = lpBuffer;
2212 AnsiString.MaximumLength = MaxLength;
2213 Status = BasepUnicodeStringTo8BitString(&AnsiString, StaticString, FALSE);
2214 if (!NT_SUCCESS(Status))
2215 {
2216 BaseSetLastNTError(Status);
2217 return 0;
2218 }
2219
2220 return AnsiString.Length;
2221 }
2222
2223 /*
2224 * @implemented
2225 */
2226 DWORD
2227 WINAPI
2228 GetCurrentDirectoryW(IN DWORD nBufferLength,
2229 IN LPWSTR lpBuffer)
2230 {
2231 return RtlGetCurrentDirectory_U(nBufferLength * sizeof(WCHAR), lpBuffer) / sizeof(WCHAR);
2232 }
2233
2234 /*
2235 * @implemented
2236 */
2237 BOOL
2238 WINAPI
2239 SetCurrentDirectoryA(IN LPCSTR lpPathName)
2240 {
2241 PUNICODE_STRING DirName;
2242 NTSTATUS Status;
2243
2244 if (!lpPathName)
2245 {
2246 BaseSetLastNTError(STATUS_INVALID_PARAMETER);
2247 return FALSE;
2248 }
2249
2250 DirName = Basep8BitStringToStaticUnicodeString(lpPathName);
2251 if (!DirName) return FALSE;
2252
2253 if (CheckForSameCurdir(DirName)) return FALSE;
2254
2255 Status = RtlSetCurrentDirectory_U(DirName);
2256 if (NT_SUCCESS(Status)) return TRUE;
2257
2258 if ((*DirName->Buffer != L'"') || (DirName->Length <= 2))
2259 {
2260 BaseSetLastNTError(Status);
2261 return 0;
2262 }
2263
2264 DirName = Basep8BitStringToStaticUnicodeString(lpPathName + 1);
2265 if (!DirName) return FALSE;
2266
2267 Status = RtlSetCurrentDirectory_U(DirName);
2268 if (!NT_SUCCESS(Status))
2269 {
2270 BaseSetLastNTError(Status);
2271 return FALSE;
2272 }
2273
2274 return TRUE;
2275 }
2276
2277 /*
2278 * @implemented
2279 */
2280 BOOL
2281 WINAPI
2282 SetCurrentDirectoryW(IN LPCWSTR lpPathName)
2283 {
2284 NTSTATUS Status;
2285 UNICODE_STRING UnicodeString;
2286
2287 if (!lpPathName)
2288 {
2289 BaseSetLastNTError(STATUS_INVALID_PARAMETER);
2290 return FALSE;
2291 }
2292
2293 Status = RtlInitUnicodeStringEx(&UnicodeString, lpPathName);
2294 if (NT_SUCCESS(Status))
2295 {
2296 if (!CheckForSameCurdir(&UnicodeString))
2297 {
2298 Status = RtlSetCurrentDirectory_U(&UnicodeString);
2299 }
2300 }
2301
2302 if (!NT_SUCCESS(Status))
2303 {
2304 BaseSetLastNTError(Status);
2305 return FALSE;
2306 }
2307
2308 return TRUE;
2309 }
2310
2311 /*
2312 * @implemented
2313 */
2314 UINT
2315 WINAPI
2316 GetSystemDirectoryA(IN LPSTR lpBuffer,
2317 IN UINT uSize)
2318 {
2319 ANSI_STRING AnsiString;
2320 NTSTATUS Status;
2321 ULONG AnsiLength;
2322
2323 /* Get the correct size of the Unicode Base directory */
2324 Status = RtlUnicodeToMultiByteSize(&AnsiLength,
2325 BaseWindowsSystemDirectory.Buffer,
2326 BaseWindowsSystemDirectory.MaximumLength);
2327 if (!NT_SUCCESS(Status)) return 0;
2328
2329 if (uSize < AnsiLength) return AnsiLength;
2330
2331 RtlInitEmptyAnsiString(&AnsiString, lpBuffer, uSize);
2332
2333 Status = BasepUnicodeStringTo8BitString(&AnsiString,
2334 &BaseWindowsSystemDirectory,
2335 FALSE);
2336 if (!NT_SUCCESS(Status)) return 0;
2337
2338 return AnsiString.Length;
2339 }
2340
2341 /*
2342 * @implemented
2343 */
2344 UINT
2345 WINAPI
2346 GetSystemDirectoryW(IN LPWSTR lpBuffer,
2347 IN UINT uSize)
2348 {
2349 ULONG ReturnLength;
2350
2351 ReturnLength = BaseWindowsSystemDirectory.MaximumLength;
2352 if ((uSize * sizeof(WCHAR)) >= ReturnLength)
2353 {
2354 RtlCopyMemory(lpBuffer,
2355 BaseWindowsSystemDirectory.Buffer,
2356 BaseWindowsSystemDirectory.Length);
2357 lpBuffer[BaseWindowsSystemDirectory.Length / sizeof(WCHAR)] = ANSI_NULL;
2358
2359 ReturnLength = BaseWindowsSystemDirectory.Length;
2360 }
2361
2362 return ReturnLength / sizeof(WCHAR);
2363 }
2364
2365 /*
2366 * @implemented
2367 */
2368 UINT
2369 WINAPI
2370 GetWindowsDirectoryA(IN LPSTR lpBuffer,
2371 IN UINT uSize)
2372 {
2373 /* Is this a TS installation? */
2374 if (gpTermsrvGetWindowsDirectoryA) UNIMPLEMENTED;
2375
2376 /* Otherwise, call the System API */
2377 return GetSystemWindowsDirectoryA(lpBuffer, uSize);
2378 }
2379
2380 /*
2381 * @implemented
2382 */
2383 UINT
2384 WINAPI
2385 GetWindowsDirectoryW(IN LPWSTR lpBuffer,
2386 IN UINT uSize)
2387 {
2388 /* Is this a TS installation? */
2389 if (gpTermsrvGetWindowsDirectoryW) UNIMPLEMENTED;
2390
2391 /* Otherwise, call the System API */
2392 return GetSystemWindowsDirectoryW(lpBuffer, uSize);
2393 }
2394
2395 /*
2396 * @implemented
2397 */
2398 UINT
2399 WINAPI
2400 GetSystemWindowsDirectoryA(IN LPSTR lpBuffer,
2401 IN UINT uSize)
2402 {
2403 ANSI_STRING AnsiString;
2404 NTSTATUS Status;
2405 ULONG AnsiLength;
2406
2407 /* Get the correct size of the Unicode Base directory */
2408 Status = RtlUnicodeToMultiByteSize(&AnsiLength,
2409 BaseWindowsDirectory.Buffer,
2410 BaseWindowsDirectory.MaximumLength);
2411 if (!NT_SUCCESS(Status)) return 0;
2412
2413 if (uSize < AnsiLength) return AnsiLength;
2414
2415 RtlInitEmptyAnsiString(&AnsiString, lpBuffer, uSize);
2416
2417 Status = BasepUnicodeStringTo8BitString(&AnsiString,
2418 &BaseWindowsDirectory,
2419 FALSE);
2420 if (!NT_SUCCESS(Status)) return 0;
2421
2422 return AnsiString.Length;
2423 }
2424
2425 /*
2426 * @implemented
2427 */
2428 UINT
2429 WINAPI
2430 GetSystemWindowsDirectoryW(IN LPWSTR lpBuffer,
2431 IN UINT uSize)
2432 {
2433 ULONG ReturnLength;
2434
2435 ReturnLength = BaseWindowsDirectory.MaximumLength;
2436 if ((uSize * sizeof(WCHAR)) >= ReturnLength)
2437 {
2438 RtlCopyMemory(lpBuffer,
2439 BaseWindowsDirectory.Buffer,
2440 BaseWindowsDirectory.Length);
2441 lpBuffer[BaseWindowsDirectory.Length / sizeof(WCHAR)] = ANSI_NULL;
2442
2443 ReturnLength = BaseWindowsDirectory.Length;
2444 }
2445
2446 return ReturnLength / sizeof(WCHAR);
2447 }
2448
2449 /*
2450 * @unimplemented
2451 */
2452 UINT
2453 WINAPI
2454 GetSystemWow64DirectoryW(IN LPWSTR lpBuffer,
2455 IN UINT uSize)
2456 {
2457 #ifdef _WIN64
2458 UNIMPLEMENTED;
2459 return 0;
2460 #else
2461 SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
2462 return 0;
2463 #endif
2464 }
2465
2466 /*
2467 * @unimplemented
2468 */
2469 UINT
2470 WINAPI
2471 GetSystemWow64DirectoryA(IN LPSTR lpBuffer,
2472 IN UINT uSize)
2473 {
2474 #ifdef _WIN64
2475 UNIMPLEMENTED;
2476 return 0;
2477 #else
2478 SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
2479 return 0;
2480 #endif
2481 }
2482
2483 /* EOF */