Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=47766 Signed-off-by: Zhiyi Zhang zzhang@codeweavers.com --- dlls/kernelbase/path.c | 168 ++++++++++++++++++++++------------- dlls/kernelbase/tests/path.c | 34 +++++++ 2 files changed, 138 insertions(+), 64 deletions(-)
diff --git a/dlls/kernelbase/path.c b/dlls/kernelbase/path.c index 82708be544..e2cff64f5a 100644 --- a/dlls/kernelbase/path.c +++ b/dlls/kernelbase/path.c @@ -200,8 +200,9 @@ static const WCHAR *get_root_end(const WCHAR *path)
HRESULT WINAPI PathAllocCanonicalize(const WCHAR *path_in, DWORD flags, WCHAR **path_out) { - WCHAR *buffer, *dst; - const WCHAR *src; + WCHAR *buffer, *dst, *old_dst; + const WCHAR *src, *old_src; + const WCHAR *segment_start; const WCHAR *root_end; SIZE_T buffer_size, length;
@@ -263,78 +264,89 @@ HRESULT WINAPI PathAllocCanonicalize(const WCHAR *path_in, DWORD flags, WCHAR ** dst += root_end - buffer + 1; }
- while (*src) + /* If segment normalization is on, remove trailing dots in the root segment */ + if (!(flags & PATHCCH_DO_NOT_NORMALIZE_SEGMENTS)) { - if (src[0] == '.') + old_src = src; + while (*src && *src == '.') + ++src; + + /* Not trailing dots, don't remove */ + if (*src) + { + src = old_src; + } + /* At least have one dot */ + else if (src > old_src) { - if (src[1] == '.') + /* If X:\ is incomplete, complete it */ + if (iswalpha(buffer[0]) && buffer[1] == ':' && buffer[2] != '\') { - /* Keep one . after * */ - if (dst > buffer && dst[-1] == '*') - { - *dst++ = *src++; - continue; - } + root_end = buffer + 2; + dst = buffer + 3; + buffer[2] = '\'; + /* If the next character is , skip it to avoid duplicated \ */ + if (src[0] == '\') + ++src; + } + } + }
- /* Keep the . if one of the following is true: - * 1. PATHCCH_DO_NOT_NORMALIZE_SEGMENTS - * 2. in form of a..b - */ - if (dst > buffer - && (((flags & PATHCCH_DO_NOT_NORMALIZE_SEGMENTS) && dst[-1] != '\') - || (dst[-1] != '\' && src[2] != '\' && src[2]))) - { - *dst++ = *src++; - *dst++ = *src++; - continue; - } + /* Dots after X: are part of the root segment */ + if (iswalpha(buffer[0]) && buffer[1] == ':' && !buffer[2]) + { + while (*src && *src == '.') + *dst++ = *src++; + }
- /* Remove the \ before .. if the \ is not part of root */ - if (dst > buffer && dst[-1] == '\' && (!root_end || dst - 1 > root_end)) + while (*src) + { + /* .. up one level */ + if (src[0] == '.' && src[1] == '.' && (src[2] == '\' || !src[2])) + { + /* Remove the \ before .. if the \ is not part of root */ + if (dst > buffer && dst[-1] == '\' && (!root_end || dst - 1 > root_end)) + { + *--dst = 0; + /* Remove characters until a \ is encountered */ + while (dst > buffer) { - *--dst = '\0'; - /* Remove characters until a \ is encountered */ - while (dst > buffer) + if (dst[-1] == '\') { - if (dst[-1] == '\') - { - *--dst = 0; - break; - } - else - *--dst = 0; + *--dst = 0; + break; } + else + *--dst = 0; } - /* Remove the extra \ after .. if the \ before .. wasn't deleted */ - else if (src[2] == '\') - src++; - - src += 2; } - else - { - /* Keep the . if one of the following is true: - * 1. PATHCCH_DO_NOT_NORMALIZE_SEGMENTS - * 2. in form of a.b, which is used in domain names - * 3. *. - */ - if (dst > buffer - && ((flags & PATHCCH_DO_NOT_NORMALIZE_SEGMENTS && dst[-1] != '\') - || (dst[-1] != '\' && src[1] != '\' && src[1]) || (dst[-1] == '*'))) - { - *dst++ = *src++; - continue; - } - - /* Remove the \ before . if the \ is not part of root */ - if (dst > buffer && dst[-1] == '\' && (!root_end || dst - 1 > root_end)) dst--; - /* Remove the extra \ after . if the \ before . wasn't deleted */ - else if (src[1] == '\') - src++; + /* Remove the extra \ after .. if the \ before .. wasn't deleted */ + else if (src[2] == '\') + ++src;
- src++; + src += 2; + /* If X:\ is not complete, then complete it */ + if (iswalpha(buffer[0]) && buffer[1] == ':' && buffer[2] != '\') + { + root_end = buffer + 2; + dst = buffer + 3; + buffer[2] = '\'; + /* If the next character is , skip it to avoid duplicated \ */ + if (src[0] == '\') + ++src; } - + } + /* . current level */ + else if (src[0] == '.' && (src[1] == '\' || !src[1])) + { + /* Remove the \ before . if the \ is not part of root */ + if (dst > buffer && dst[-1] == '\' && (!root_end || dst - 1 > root_end)) + *--dst = 0; + /* Remove the extra \ after . if the \ before . wasn't deleted */ + else if (src[1] == '\') + ++src; + + ++src; /* If X:\ is not complete, then complete it */ if (iswalpha(buffer[0]) && buffer[1] == ':' && buffer[2] != '\') { @@ -345,9 +357,37 @@ HRESULT WINAPI PathAllocCanonicalize(const WCHAR *path_in, DWORD flags, WCHAR ** if (src[0] == '\') src++; } } - /* Copy over */ - else + /* *.+ */ + else if (!(flags & PATHCCH_DO_NOT_NORMALIZE_SEGMENTS) && src[0] == '*' && src[1] == '.') + { + /* Keep only one . after * */ *dst++ = *src++; + *dst++ = *src++; + while (*src == '.') + ++src; + } + /* Normal segments */ + else + { + segment_start = dst; + /* Copy the whole segment */ + while (*src && *src != '\') + *dst++ = *src++; + + /* Remove trailing dots, aka segment normalization */ + if (!(flags & PATHCCH_DO_NOT_NORMALIZE_SEGMENTS)) + { + old_dst = dst; + while (dst > segment_start && (dst[-1] == '.')) + --dst; + /* Not trailing dots, don't remove */ + if (dst == segment_start) + dst = old_dst; + } + + if (*src == '\') + *dst++ = *src++; + } } /* End the path */ *dst = 0; diff --git a/dlls/kernelbase/tests/path.c b/dlls/kernelbase/tests/path.c index b5da303629..d7492a1e9f 100644 --- a/dlls/kernelbase/tests/path.c +++ b/dlls/kernelbase/tests/path.c @@ -72,6 +72,7 @@ static const struct alloccanonicalize_test alloccanonicalize_tests[] = {"\\?C:a", "\\?C:a", 0, S_OK},
/* No . */ + {"*", "*", 0, S_OK}, {"", "\", 0, S_OK}, {"C:", "C:", 0, S_OK}, {"C:\", "C:\", 0, S_OK}, @@ -85,17 +86,26 @@ static const struct alloccanonicalize_test alloccanonicalize_tests[] = {"*.", "*.", 0, S_OK}, {"*..", "*.", 0, S_OK}, {"*...", "*.", 0, S_OK}, + {"*....", "*.", 0, S_OK}, + {".a", ".a", 0, S_OK}, {"a.", "a", 0, S_OK}, + {".a.", ".a", 0, S_OK}, {"a.b", "a.b", 0, S_OK}, + {".a.b.", ".a.b", 0, S_OK}, {"a\.", "a", 0, S_OK}, {"a\.\b", "a\b", 0, S_OK}, + {":.", ":", 0, S_OK}, {"C:.", "C:\", 0, S_OK}, + {"C:.\", "C:.\", 0, S_OK}, + {"C:.\.", "C:\", 0, S_OK}, {"C:\.", "C:\", 0, S_OK}, {"C:\.\", "C:\", 0, S_OK}, {"C:\a.", "C:\a", 0, S_OK}, + {"C:\.a", "C:\.a", 0, S_OK}, {"C:\a\.", "C:\a", 0, S_OK}, {"C:\a\\.", "C:\a\", 0, S_OK}, {"C:\a\\\.", "C:\a\\", 0, S_OK}, + {".\", "\", 0, S_OK}, {"\.", "\", 0, S_OK}, {"\\.", "\\", 0, S_OK}, {"\\.\", "\\", 0, S_OK}, @@ -115,21 +125,42 @@ static const struct alloccanonicalize_test alloccanonicalize_tests[] = "\\?\Volume{e51a1864-6f2d-4019-b73d-f4e60e600c26}\", 0, S_OK},
/* .. */ + {"..a", "..a", 0, S_OK}, + {"...a", "...a", 0, S_OK}, + {"....a", "....a", 0, S_OK}, {"a..", "a", 0, S_OK}, + {"a...", "a", 0, S_OK}, + {"a....", "a", 0, S_OK}, + {"..a..", "..a", 0, S_OK}, {"a..b", "a..b", 0, S_OK}, + {"..a..b..", "..a..b", 0, S_OK}, {"a\..", "\", 0, S_OK}, {"a\..\", "\", 0, S_OK}, {"a\..\b", "\b", 0, S_OK}, + {":..", ":", 0, S_OK}, {"C:..", "C:\", 0, S_OK}, + {"C:...", "C:\", 0, S_OK}, + {"C:..\", "C:..\", 0, S_OK}, + {"C:..\\", "C:..\\", 0, S_OK}, + {"C:...\", "C:...\", 0, S_OK}, {"C:\..", "C:\", 0, S_OK}, + {"C:\..a", "C:\..a", 0, S_OK}, + {"C:\...a", "C:\...a", 0, S_OK}, + {"C:\....a", "C:\....a", 0, S_OK}, + {"C:\a..", "C:\a", 0, S_OK}, {"C:\\..", "C:\", 0, S_OK}, {"C:\..\", "C:\", 0, S_OK}, + {"C:\...\", "C:\...\", 0, S_OK}, {"C:\a\..", "C:\", 0, S_OK}, + {"C:\a\b..", "C:\a\b", 0, S_OK}, {"C:\a\\..", "C:\a", 0, S_OK}, {"C:\a\\\..", "C:\a\", 0, S_OK}, {"C:\a\..\b", "C:\b", 0, S_OK}, {"C:\a\..\\b", "C:\\b", 0, S_OK}, + {"..\", "\", 0, S_OK}, + {"...\", "...\", 0, S_OK}, {"\..", "\", 0, S_OK}, + {"\...", "\", 0, S_OK}, {"\\..", "\\", 0, S_OK}, {"\\\..", "\", 0, S_OK}, {"\\..\", "\\", 0, S_OK}, @@ -249,6 +280,8 @@ static const struct alloccanonicalize_test alloccanonicalize_tests[] =
/* PATHCCH_DO_NOT_NORMALIZE_SEGMENTS */ /* No effect for spaces */ + {"a ", "a ", 0, S_OK}, + {"C:\a ", "C:\a ", 0, S_OK}, {"C:\a \", "C:\a \", PATHCCH_DO_NOT_NORMALIZE_SEGMENTS, S_OK}, {"C:\a\ ", "C:\a\ ", PATHCCH_DO_NOT_NORMALIZE_SEGMENTS, S_OK}, {"C:\a ", "C:\a ", PATHCCH_DO_NOT_NORMALIZE_SEGMENTS, S_OK}, @@ -265,6 +298,7 @@ static const struct alloccanonicalize_test alloccanonicalize_tests[] = {"..", "\", PATHCCH_DO_NOT_NORMALIZE_SEGMENTS, S_OK}, {"C:.", "C:.", PATHCCH_DO_NOT_NORMALIZE_SEGMENTS, S_OK}, {"C:..", "C:..", PATHCCH_DO_NOT_NORMALIZE_SEGMENTS, S_OK}, + {"C:...", "C:...", PATHCCH_DO_NOT_NORMALIZE_SEGMENTS, S_OK}, {"C:\a\.", "C:\a", PATHCCH_DO_NOT_NORMALIZE_SEGMENTS, S_OK}, {"C:\a\..", "C:\", PATHCCH_DO_NOT_NORMALIZE_SEGMENTS, S_OK}, {"C:\a.", "C:\a.", PATHCCH_DO_NOT_NORMALIZE_SEGMENTS, S_OK},