Index: lib/rtl/path.c =================================================================== --- lib/rtl/path.c (révision 59193) +++ lib/rtl/path.c (copie de travail) @@ -284,6 +284,291 @@ return Status; } + + + + +/****************************************************************************** + ** ** + ** WARNING !! WINE CODE FOLLOWS !! UNSTABILITIES CAN APPEAR !! ** + ** ** + ******************************************************************************/ + +/****************************************************************** + * collapse_path + * + * Helper for RtlGetFullPathName_U. + * Get rid of . and .. components in the path. + */ +void FORCEINLINE collapse_path( WCHAR *path, UINT mark ) +{ + WCHAR *p, *next; + + /* convert every / into a \ */ + for (p = path; *p; p++) if (*p == '/') *p = '\\'; + + /* collapse duplicate backslashes */ + next = path + max( 1, mark ); + for (p = next; *p; p++) if (*p != '\\' || next[-1] != '\\') *next++ = *p; + *next = 0; + + p = path + mark; + while (*p) + { + if (*p == '.') + { + switch(p[1]) + { + case '\\': /* .\ component */ + next = p + 2; + memmove( p, next, (wcslen(next) + 1) * sizeof(WCHAR) ); + continue; + case 0: /* final . */ + if (p > path + mark) p--; + *p = 0; + continue; + case '.': + if (p[2] == '\\') /* ..\ component */ + { + next = p + 3; + if (p > path + mark) + { + p--; + while (p > path + mark && p[-1] != '\\') p--; + } + memmove( p, next, (wcslen(next) + 1) * sizeof(WCHAR) ); + continue; + } + else if (!p[2]) /* final .. */ + { + if (p > path + mark) + { + p--; + while (p > path + mark && p[-1] != '\\') p--; + if (p > path + mark) p--; + } + *p = 0; + continue; + } + break; + } + } + /* skip to the next component */ + while (*p && *p != '\\') p++; + if (*p == '\\') + { + /* remove last dot in previous dir name */ + if (p > path + mark && p[-1] == '.') memmove( p-1, p, (wcslen(p) + 1) * sizeof(WCHAR) ); + else p++; + } + } + + /* remove trailing spaces and dots (yes, Windows really does that, don't ask) */ + while (p > path + mark && (p[-1] == ' ' || p[-1] == '.')) p--; + *p = 0; +} + +/****************************************************************** + * skip_unc_prefix + * + * Skip the \\share\dir\ part of a file name. Helper for RtlGetFullPathName_U. + */ +static const WCHAR *skip_unc_prefix( const WCHAR *ptr ) +{ + ptr += 2; + while (*ptr && !IS_PATH_SEPARATOR(*ptr)) ptr++; /* share name */ + while (IS_PATH_SEPARATOR(*ptr)) ptr++; + while (*ptr && !IS_PATH_SEPARATOR(*ptr)) ptr++; /* dir name */ + while (IS_PATH_SEPARATOR(*ptr)) ptr++; + return ptr; +} + +/****************************************************************** + * get_full_path_helper + * + * Helper for RtlGetFullPathName_U + * Note: name and buffer are allowed to point to the same memory spot + */ +static ULONG get_full_path_helper( + LPCWSTR name, + LPWSTR buffer, + ULONG size) +{ + SIZE_T reqsize = 0, mark = 0, dep = 0, deplen; + LPWSTR ins_str = NULL; + LPCWSTR ptr; + const UNICODE_STRING* cd; + WCHAR tmp[4]; + + /* return error if name only consists of spaces */ + for (ptr = name; *ptr; ptr++) if (*ptr != ' ') break; + if (!*ptr) return 0; + + RtlAcquirePebLock(); + + //cd = &((PCURDIR)&NtCurrentTeb()->ProcessEnvironmentBlock->ProcessParameters->CurrentDirectory.DosPath)->DosPath; + cd = &NtCurrentTeb()->ProcessEnvironmentBlock->ProcessParameters->CurrentDirectory.DosPath; + + switch (RtlDetermineDosPathNameType_U(name)) + { + case RtlPathTypeUncAbsolute: /* \\foo */ + ptr = skip_unc_prefix( name ); + mark = (ptr - name); + break; + + case RtlPathTypeLocalDevice: /* \\.\foo */ + mark = 4; + break; + + case RtlPathTypeDriveAbsolute: /* c:\foo */ + reqsize = sizeof(WCHAR); + tmp[0] = towupper(name[0]); + ins_str = tmp; + dep = 1; + mark = 3; + break; + + case RtlPathTypeDriveRelative: /* c:foo */ + dep = 2; + if (towupper(name[0]) != towupper(cd->Buffer[0]) || cd->Buffer[1] != ':') + { + UNICODE_STRING var, val; + + tmp[0] = '='; + tmp[1] = name[0]; + tmp[2] = ':'; + tmp[3] = '\0'; + var.Length = 3 * sizeof(WCHAR); + var.MaximumLength = 4 * sizeof(WCHAR); + var.Buffer = tmp; + val.Length = 0; + val.MaximumLength = (USHORT)size; + val.Buffer = RtlAllocateHeap(RtlGetProcessHeap(), 0, size); + if (val.Buffer == NULL) + { + reqsize = 0; + goto done; + } + + switch (RtlQueryEnvironmentVariable_U(NULL, &var, &val)) + { + case STATUS_SUCCESS: + /* FIXME: Win2k seems to check that the environment variable actually points + * to an existing directory. If not, root of the drive is used + * (this seems also to be the only spot in RtlGetFullPathName that the + * existence of a part of a path is checked) + */ + /* fall through */ + case STATUS_BUFFER_TOO_SMALL: + reqsize = val.Length + sizeof(WCHAR); /* append trailing '\\' */ + val.Buffer[val.Length / sizeof(WCHAR)] = '\\'; + ins_str = val.Buffer; + break; + case STATUS_VARIABLE_NOT_FOUND: + reqsize = 3 * sizeof(WCHAR); + tmp[0] = name[0]; + tmp[1] = ':'; + tmp[2] = '\\'; + ins_str = tmp; + RtlFreeHeap(RtlGetProcessHeap(), 0, val.Buffer); + break; + default: + DPRINT1("Unsupported status code\n"); + RtlFreeHeap(RtlGetProcessHeap(), 0, val.Buffer); + break; + } + mark = 3; + break; + } + /* fall through */ + + case RtlPathTypeRelative: /* foo */ + reqsize = cd->Length; + ins_str = cd->Buffer; + if (cd->Buffer[1] != ':') + { + ptr = skip_unc_prefix( cd->Buffer ); + mark = ptr - cd->Buffer; + } + else mark = 3; + break; + + case RtlPathTypeRooted: /* \xxx */ +#ifdef __WINE__ + if (name[0] == '/') /* may be a Unix path */ + { + const WCHAR *ptr = name; + int drive = find_drive_root( &ptr ); + if (drive != -1) + { + reqsize = 3 * sizeof(WCHAR); + tmp[0] = 'A' + drive; + tmp[1] = ':'; + tmp[2] = '\\'; + ins_str = tmp; + mark = 3; + dep = ptr - name; + break; + } + } +#endif + if (cd->Buffer[1] == ':') + { + reqsize = 2 * sizeof(WCHAR); + tmp[0] = cd->Buffer[0]; + tmp[1] = ':'; + ins_str = tmp; + mark = 3; + } + else + { + ptr = skip_unc_prefix( cd->Buffer ); + reqsize = (ptr - cd->Buffer) * sizeof(WCHAR); + mark = reqsize / sizeof(WCHAR); + ins_str = cd->Buffer; + } + break; + + case RtlPathTypeRootLocalDevice: /* \\. */ + reqsize = 4 * sizeof(WCHAR); + dep = 3; + tmp[0] = '\\'; + tmp[1] = '\\'; + tmp[2] = '.'; + tmp[3] = '\\'; + ins_str = tmp; + mark = 4; + break; + + case RtlPathTypeUnknown: + goto done; + } + + /* enough space ? */ + deplen = wcslen(name + dep) * sizeof(WCHAR); + if (reqsize + deplen + sizeof(WCHAR) > size) + { + /* not enough space, return need size (including terminating '\0') */ + reqsize += deplen + sizeof(WCHAR); + goto done; + } + + memmove(buffer + reqsize / sizeof(WCHAR), name + dep, deplen + sizeof(WCHAR)); + if (reqsize) memcpy(buffer, ins_str, reqsize); + ////// ADDED HERE, NOT IN WINE!!! reqsize += deplen; ////// + + if (ins_str && ins_str != tmp && ins_str != cd->Buffer) + RtlFreeHeap(RtlGetProcessHeap(), 0, ins_str); + + collapse_path( buffer, (ULONG)mark ); + reqsize = wcslen(buffer) * sizeof(WCHAR); + +done: + RtlReleasePebLock(); + return (ULONG)reqsize; +} + + ULONG NTAPI RtlGetFullPathName_Ustr( @@ -299,6 +584,18 @@ WCHAR c; NTSTATUS Status; + /****** HERE IS SOME WINE CODE ******/ + WCHAR* ptr; + // ULONG dosdev; + ULONG reqsize; +#if 0 + /* Validate the input */ + if (!FileName || !Buffer) return 0; + + /* Zero out the short name */ + if (ShortName) *ShortName = NULL; +#endif + /* For now, assume the name is valid */ DPRINT("Filename: %wZ\n", FileName); DPRINT("Size and buffer: %lx %p\n", Size, Buffer); @@ -311,7 +608,7 @@ /* Break filename into component parts */ FileNameBuffer = FileName->Buffer; FileNameLength = FileName->Length; - FileNameChars = FileNameLength / sizeof(WCHAR); + FileNameChars = FileNameLength / sizeof(WCHAR); /* Kill trailing spaces */ c = FileNameBuffer[FileNameChars - 1]; @@ -338,7 +635,7 @@ DosLength = DosLength & 0xFFFF; /* Do we have a DOS length, and does the caller want validity? */ - if ((InvalidName) && (DosLengthOffset)) + if (InvalidName && (DosLengthOffset != 0)) { /* Do the check */ Status = RtlpCheckDeviceName(FileName, DosLengthOffset, InvalidName); @@ -376,7 +673,60 @@ /* This is disgusting... but avoids re-writing everything */ DPRINT("Calling old API with '%S' and %lu and %S\n", FileNameBuffer, Size, Buffer); - return RtlGetFullPathName_U(FileNameBuffer, Size, Buffer, (PWSTR*)ShortName); + /// return RtlGetFullPathName_U(FileNameBuffer, Size, Buffer, (PWSTR*)ShortName); + + + + /********************************************** + ** HERE WINE CODE BEGINS!! ** + **********************************************/ + + /* Zero out the destination buffer (implies that "name" should be different from "buffer" to get this function well-behaving) */ + RtlZeroMemory(Buffer, Size); + + if (ShortName) *ShortName = NULL; + +#if 0 + /* check for DOS device name */ + dosdev = RtlIsDosDeviceName_U(FileNameBuffer); + if (dosdev) + { + DWORD offset = HIWORD(dosdev) / sizeof(WCHAR); /* get it in WCHARs, not bytes */ + DWORD sz = LOWORD(dosdev); /* in bytes */ + + if (8 + sz + 2 > Size) return sz + 10; + wcscpy(Buffer, DeviceRootW); + memmove(Buffer + 4, FileNameBuffer + offset, sz); + Buffer[4 + sz / sizeof(WCHAR)] = '\0'; + /* ShortName isn't set in this case */ + return sz + 8; + } +#endif + + reqsize = get_full_path_helper(FileNameBuffer, Buffer, Size); + if (!reqsize) return 0; + if (reqsize > Size) + { + LPWSTR tmp = RtlAllocateHeap(RtlGetProcessHeap(), 0, reqsize); + if (tmp == NULL) return 0; + reqsize = get_full_path_helper(FileNameBuffer, tmp, reqsize); + if (reqsize + sizeof(WCHAR) > Size) /* it may have worked the second time */ + { + RtlFreeHeap(RtlGetProcessHeap(), 0, tmp); + return reqsize + sizeof(WCHAR); + } + memcpy(Buffer, tmp, reqsize + sizeof(WCHAR)); + RtlFreeHeap(RtlGetProcessHeap(), 0, tmp); + } + + /* find file part */ + if (ShortName) + { + ptr = wcsrchr(Buffer, '\\'); + if (ptr && ptr >= Buffer + 2 && *++ptr) *ShortName = ptr; + } + + return reqsize; } NTSTATUS @@ -992,8 +1342,6 @@ return Length * sizeof(WCHAR); } - - /* * @implemented */ @@ -1199,284 +1547,6 @@ /****************************************************************** - * collapse_path - * - * Helper for RtlGetFullPathName_U. - * Get rid of . and .. components in the path. - */ -void FORCEINLINE collapse_path( WCHAR *path, UINT mark ) -{ - WCHAR *p, *next; - - /* convert every / into a \ */ - for (p = path; *p; p++) if (*p == '/') *p = '\\'; - - /* collapse duplicate backslashes */ - next = path + max( 1, mark ); - for (p = next; *p; p++) if (*p != '\\' || next[-1] != '\\') *next++ = *p; - *next = 0; - - p = path + mark; - while (*p) - { - if (*p == '.') - { - switch(p[1]) - { - case '\\': /* .\ component */ - next = p + 2; - memmove( p, next, (wcslen(next) + 1) * sizeof(WCHAR) ); - continue; - case 0: /* final . */ - if (p > path + mark) p--; - *p = 0; - continue; - case '.': - if (p[2] == '\\') /* ..\ component */ - { - next = p + 3; - if (p > path + mark) - { - p--; - while (p > path + mark && p[-1] != '\\') p--; - } - memmove( p, next, (wcslen(next) + 1) * sizeof(WCHAR) ); - continue; - } - else if (!p[2]) /* final .. */ - { - if (p > path + mark) - { - p--; - while (p > path + mark && p[-1] != '\\') p--; - if (p > path + mark) p--; - } - *p = 0; - continue; - } - break; - } - } - /* skip to the next component */ - while (*p && *p != '\\') p++; - if (*p == '\\') - { - /* remove last dot in previous dir name */ - if (p > path + mark && p[-1] == '.') memmove( p-1, p, (wcslen(p) + 1) * sizeof(WCHAR) ); - else p++; - } - } - - /* remove trailing spaces and dots (yes, Windows really does that, don't ask) */ - while (p > path + mark && (p[-1] == ' ' || p[-1] == '.')) p--; - *p = 0; -} - - - -/****************************************************************** - * skip_unc_prefix - * - * Skip the \\share\dir\ part of a file name. Helper for RtlGetFullPathName_U. - */ -static const WCHAR *skip_unc_prefix( const WCHAR *ptr ) -{ - ptr += 2; - while (*ptr && !IS_PATH_SEPARATOR(*ptr)) ptr++; /* share name */ - while (IS_PATH_SEPARATOR(*ptr)) ptr++; - while (*ptr && !IS_PATH_SEPARATOR(*ptr)) ptr++; /* dir name */ - while (IS_PATH_SEPARATOR(*ptr)) ptr++; - return ptr; -} - - -/****************************************************************** - * get_full_path_helper - * - * Helper for RtlGetFullPathName_U - * Note: name and buffer are allowed to point to the same memory spot - */ -static ULONG get_full_path_helper( - LPCWSTR name, - LPWSTR buffer, - ULONG size) -{ - SIZE_T reqsize = 0, mark = 0, dep = 0, deplen; - LPWSTR ins_str = NULL; - LPCWSTR ptr; - const UNICODE_STRING* cd; - WCHAR tmp[4]; - - /* return error if name only consists of spaces */ - for (ptr = name; *ptr; ptr++) if (*ptr != ' ') break; - if (!*ptr) return 0; - - RtlAcquirePebLock(); - - //cd = &((PCURDIR)&NtCurrentTeb()->ProcessEnvironmentBlock->ProcessParameters->CurrentDirectory.DosPath)->DosPath; - cd = &NtCurrentTeb()->ProcessEnvironmentBlock->ProcessParameters->CurrentDirectory.DosPath; - - switch (RtlDetermineDosPathNameType_U(name)) - { - case RtlPathTypeUncAbsolute: /* \\foo */ - ptr = skip_unc_prefix( name ); - mark = (ptr - name); - break; - - case RtlPathTypeLocalDevice: /* \\.\foo */ - mark = 4; - break; - - case RtlPathTypeDriveAbsolute: /* c:\foo */ - reqsize = sizeof(WCHAR); - tmp[0] = towupper(name[0]); - ins_str = tmp; - dep = 1; - mark = 3; - break; - - case RtlPathTypeDriveRelative: /* c:foo */ - dep = 2; - if (towupper(name[0]) != towupper(cd->Buffer[0]) || cd->Buffer[1] != ':') - { - UNICODE_STRING var, val; - - tmp[0] = '='; - tmp[1] = name[0]; - tmp[2] = ':'; - tmp[3] = '\0'; - var.Length = 3 * sizeof(WCHAR); - var.MaximumLength = 4 * sizeof(WCHAR); - var.Buffer = tmp; - val.Length = 0; - val.MaximumLength = (USHORT)size; - val.Buffer = RtlAllocateHeap(RtlGetProcessHeap(), 0, size); - if (val.Buffer == NULL) - { - reqsize = 0; - goto done; - } - - switch (RtlQueryEnvironmentVariable_U(NULL, &var, &val)) - { - case STATUS_SUCCESS: - /* FIXME: Win2k seems to check that the environment variable actually points - * to an existing directory. If not, root of the drive is used - * (this seems also to be the only spot in RtlGetFullPathName that the - * existence of a part of a path is checked) - */ - /* fall thru */ - case STATUS_BUFFER_TOO_SMALL: - reqsize = val.Length + sizeof(WCHAR); /* append trailing '\\' */ - val.Buffer[val.Length / sizeof(WCHAR)] = '\\'; - ins_str = val.Buffer; - break; - case STATUS_VARIABLE_NOT_FOUND: - reqsize = 3 * sizeof(WCHAR); - tmp[0] = name[0]; - tmp[1] = ':'; - tmp[2] = '\\'; - ins_str = tmp; - RtlFreeHeap(RtlGetProcessHeap(), 0, val.Buffer); - break; - default: - DPRINT1("Unsupported status code\n"); - RtlFreeHeap(RtlGetProcessHeap(), 0, val.Buffer); - break; - } - mark = 3; - break; - } - /* fall through */ - - case RtlPathTypeRelative: /* foo */ - reqsize = cd->Length; - ins_str = cd->Buffer; - if (cd->Buffer[1] != ':') - { - ptr = skip_unc_prefix( cd->Buffer ); - mark = ptr - cd->Buffer; - } - else mark = 3; - break; - - case RtlPathTypeRooted: /* \xxx */ -#ifdef __WINE__ - if (name[0] == '/') /* may be a Unix path */ - { - const WCHAR *ptr = name; - int drive = find_drive_root( &ptr ); - if (drive != -1) - { - reqsize = 3 * sizeof(WCHAR); - tmp[0] = 'A' + drive; - tmp[1] = ':'; - tmp[2] = '\\'; - ins_str = tmp; - mark = 3; - dep = ptr - name; - break; - } - } -#endif - if (cd->Buffer[1] == ':') - { - reqsize = 2 * sizeof(WCHAR); - tmp[0] = cd->Buffer[0]; - tmp[1] = ':'; - ins_str = tmp; - mark = 3; - } - else - { - ptr = skip_unc_prefix( cd->Buffer ); - reqsize = (ptr - cd->Buffer) * sizeof(WCHAR); - mark = reqsize / sizeof(WCHAR); - ins_str = cd->Buffer; - } - break; - - case RtlPathTypeRootLocalDevice: /* \\. */ - reqsize = 4 * sizeof(WCHAR); - dep = 3; - tmp[0] = '\\'; - tmp[1] = '\\'; - tmp[2] = '.'; - tmp[3] = '\\'; - ins_str = tmp; - mark = 4; - break; - - case RtlPathTypeUnknown: - goto done; - } - - /* enough space ? */ - deplen = wcslen(name + dep) * sizeof(WCHAR); - if (reqsize + deplen + sizeof(WCHAR) > size) - { - /* not enough space, return need size (including terminating '\0') */ - reqsize += deplen + sizeof(WCHAR); - goto done; - } - - memmove(buffer + reqsize / sizeof(WCHAR), name + dep, deplen + sizeof(WCHAR)); - if (reqsize) memcpy(buffer, ins_str, reqsize); - reqsize += deplen; - - if (ins_str && ins_str != tmp && ins_str != cd->Buffer) - RtlFreeHeap(RtlGetProcessHeap(), 0, ins_str); - - collapse_path( buffer, (ULONG)mark ); - reqsize = wcslen(buffer) * sizeof(WCHAR); - -done: - RtlReleasePebLock(); - return (ULONG)reqsize; -} - - -/****************************************************************** * RtlGetFullPathName_U (NTDLL.@) * * Returns the number of bytes written to buffer (not including the @@ -1488,60 +1558,33 @@ * * @implemented */ -ULONG NTAPI RtlGetFullPathName_U( - const WCHAR* name, - ULONG size, - WCHAR* buffer, - WCHAR** file_part) + +/* + * @implemented + */ +ULONG +NTAPI +RtlGetFullPathName_U( + _In_ PCWSTR FileName, + _In_ ULONG Size, + _Out_z_bytecap_(Size) PWSTR Buffer, + _Out_opt_ PWSTR *ShortName) { - WCHAR* ptr; - ULONG dosdev; - ULONG reqsize; + NTSTATUS Status; + UNICODE_STRING FileNameString; + RTL_PATH_TYPE PathType; - DPRINT("RtlGetFullPathName_U(%S %lu %p %p)\n", name, size, buffer, file_part); + /* Build the string */ + Status = RtlInitUnicodeStringEx(&FileNameString, FileName); + if (!NT_SUCCESS(Status)) return 0; - if (!name || !*name) return 0; - - /* Zero out the destination buffer (implies that "name" should be different from "buffer" to get this function well-behaving) */ - RtlZeroMemory(buffer, size); - - if (file_part) *file_part = NULL; - - /* check for DOS device name */ - dosdev = RtlIsDosDeviceName_U((WCHAR*)name); - if (dosdev) - { - DWORD offset = HIWORD(dosdev) / sizeof(WCHAR); /* get it in WCHARs, not bytes */ - DWORD sz = LOWORD(dosdev); /* in bytes */ - - if (8 + sz + 2 > size) return sz + 10; - wcscpy(buffer, DeviceRootW); - memmove(buffer + 4, name + offset, sz); - buffer[4 + sz / sizeof(WCHAR)] = '\0'; - /* file_part isn't set in this case */ - return sz + 8; - } - - reqsize = get_full_path_helper(name, buffer, size); - if (!reqsize) return 0; - if (reqsize > size) - { - LPWSTR tmp = RtlAllocateHeap(RtlGetProcessHeap(), 0, reqsize); - if (tmp == NULL) return 0; - reqsize = get_full_path_helper(name, tmp, reqsize); - if (reqsize + sizeof(WCHAR) > size) /* it may have worked the second time */ - { - RtlFreeHeap(RtlGetProcessHeap(), 0, tmp); - return reqsize + sizeof(WCHAR); - } - memcpy( buffer, tmp, reqsize + sizeof(WCHAR) ); - RtlFreeHeap(RtlGetProcessHeap(), 0, tmp); - } - - /* find file part */ - if (file_part && (ptr = wcsrchr(buffer, '\\')) != NULL && ptr >= buffer + 2 && *++ptr) - *file_part = ptr; - return reqsize; + /* Call the extended function */ + return RtlGetFullPathName_Ustr(&FileNameString, + Size, + Buffer, + (PCWSTR*)ShortName, + NULL, + &PathType); } /*