From 0dc009c4320a9cb72dc96bb60bd55970cf6d4345 Mon Sep 17 00:00:00 2001
From: Julien Schueller <schueller@phimeca.com>
Date: Wed, 4 Jul 2018 22:35:16 +0200
Subject: [PATCH] kernelbase: Implement
 PathCchRemoveBackslash()/PathCchRemoveBackslashEx().

---
 .../api-ms-win-core-path-l1-1-0.spec          |   4 +-
 dlls/kernelbase/kernelbase.spec               |   4 +-
 dlls/kernelbase/path.c                        |  31 +++++
 dlls/kernelbase/tests/path.c                  | 127 ++++++++++++++++++
 include/pathcch.h                             |   2 +
 5 files changed, 164 insertions(+), 4 deletions(-)

diff --git a/dlls/api-ms-win-core-path-l1-1-0/api-ms-win-core-path-l1-1-0.spec b/dlls/api-ms-win-core-path-l1-1-0/api-ms-win-core-path-l1-1-0.spec
index 9895b1b8e23..591ee7d882f 100644
--- a/dlls/api-ms-win-core-path-l1-1-0/api-ms-win-core-path-l1-1-0.spec
+++ b/dlls/api-ms-win-core-path-l1-1-0/api-ms-win-core-path-l1-1-0.spec
@@ -11,8 +11,8 @@
 @ stdcall PathCchCombineEx(ptr long ptr ptr long) kernelbase.PathCchCombineEx
 @ stub PathCchFindExtension
 @ stub PathCchIsRoot
-@ stub PathCchRemoveBackslash
-@ stub PathCchRemoveBackslashEx
+@ stdcall PathCchRemoveBackslash(wstr long) kernelbase.PathCchRemoveBackslash
+@ stdcall PathCchRemoveBackslashEx(wstr long ptr ptr) kernelbase.PathCchRemoveBackslashEx
 @ stub PathCchRemoveExtension
 @ stub PathCchRemoveFileSpec
 @ stub PathCchRenameExtension
diff --git a/dlls/kernelbase/kernelbase.spec b/dlls/kernelbase/kernelbase.spec
index 1f75b080378..c4a75bc1694 100644
--- a/dlls/kernelbase/kernelbase.spec
+++ b/dlls/kernelbase/kernelbase.spec
@@ -1040,8 +1040,8 @@
 @ stdcall PathCchCombineEx(ptr long ptr ptr long)
 # @ stub PathCchFindExtension
 # @ stub PathCchIsRoot
-# @ stub PathCchRemoveBackslash
-# @ stub PathCchRemoveBackslashEx
+@ stdcall PathCchRemoveBackslash(wstr long)
+@ stdcall PathCchRemoveBackslashEx(wstr long ptr ptr)
 # @ stub PathCchRemoveExtension
 # @ stub PathCchRemoveFileSpec
 # @ stub PathCchRenameExtension
diff --git a/dlls/kernelbase/path.c b/dlls/kernelbase/path.c
index 52ef7d97654..3113bfe1255 100644
--- a/dlls/kernelbase/path.c
+++ b/dlls/kernelbase/path.c
@@ -91,3 +91,34 @@ HRESULT WINAPI PathCchCombineEx(WCHAR *out, SIZE_T size, const WCHAR *path1, con
     strcpyW(out, result);
     return S_OK;
 }
+
+HRESULT WINAPI PathCchRemoveBackslash(WCHAR *path, SIZE_T size)
+{
+    return PathCchRemoveBackslashEx(path, size, NULL, NULL);
+}
+
+HRESULT WINAPI PathCchRemoveBackslashEx(WCHAR *path, SIZE_T size, WCHAR **endptr, SIZE_T *remaining)
+{
+    BOOL needs_trim;
+    SIZE_T length;
+
+    TRACE("%s, %lu, %p, %p\n", debugstr_w(path), size, endptr, remaining);
+
+    if (!path) return E_INVALIDARG;
+    length = strlenW(path);
+    needs_trim = size && length && path[length - 1] == '\\';
+
+    if (needs_trim && (length > 1) && path[length - 2] == ':')
+        needs_trim = 0;
+
+    if (needs_trim)
+    {
+        path[length - 1] = 0;
+        --length;
+    }
+
+    if (endptr) *endptr = path + length;
+    if (remaining) *remaining = size - length;
+
+    return needs_trim ? S_OK : S_FALSE;
+}
diff --git a/dlls/kernelbase/tests/path.c b/dlls/kernelbase/tests/path.c
index cdba51bf3f6..20f8852faca 100644
--- a/dlls/kernelbase/tests/path.c
+++ b/dlls/kernelbase/tests/path.c
@@ -31,6 +31,8 @@
 
 HRESULT (WINAPI *pPathCchAddBackslash)(WCHAR *out, SIZE_T size);
 HRESULT (WINAPI *pPathCchAddBackslashEx)(WCHAR *out, SIZE_T size, WCHAR **endptr, SIZE_T *remaining);
+HRESULT (WINAPI *pPathCchRemoveBackslash)(WCHAR *out, SIZE_T size);
+HRESULT (WINAPI *pPathCchRemoveBackslashEx)(WCHAR *out, SIZE_T size, WCHAR **endptr, SIZE_T *remaining);
 HRESULT (WINAPI *pPathCchCombineEx)(WCHAR *out, SIZE_T size, const WCHAR *path1, const WCHAR *path2, DWORD flags);
 
 static const struct
@@ -245,6 +247,127 @@ static void test_PathCchAddBackslashEx(void)
     }
 }
 
+struct removebackslash_test
+{
+    const char *path;
+    const char *result;
+    HRESULT hr;
+    SIZE_T size;
+    SIZE_T remaining;
+};
+
+static const struct removebackslash_test removebackslash_tests[] =
+{
+    { "C:\\",    "C:\\", S_FALSE, MAX_PATH, MAX_PATH - 3 },
+    { "a.txt\\", "a.txt", S_OK, MAX_PATH, MAX_PATH - 5 },
+    { "a/b\\",   "a/b",   S_OK, MAX_PATH, MAX_PATH - 3 },
+    { "C:\\temp\\wine.txt", "C:\\temp\\wine.txt", S_FALSE, MAX_PATH, MAX_PATH - 16 },
+};
+
+static void test_PathCchRemoveBackslash(void)
+{
+    WCHAR pathW[MAX_PATH];
+    unsigned int i;
+    HRESULT hr;
+
+    if (!pPathCchRemoveBackslash)
+    {
+        win_skip("PathCchRemoveBackslash() is not available.\n");
+        return;
+    }
+
+    pathW[0] = 0;
+    hr = pPathCchRemoveBackslash(0, 0);
+    ok(hr == E_INVALIDARG, "Unexpected hr %#x.\n", hr);
+    ok(pathW[0] == 0, "Unexpected path.\n");
+
+    pathW[0] = 0;
+    hr = pPathCchRemoveBackslash(pathW, 1);
+    ok(hr == S_FALSE, "Unexpected hr %#x.\n", hr);
+    ok(pathW[0] == 0, "Unexpected path.\n");
+
+    pathW[0] = 0;
+    hr = pPathCchRemoveBackslash(pathW, 2);
+    ok(hr == S_FALSE, "Unexpected hr %#x.\n", hr);
+    ok(pathW[0] == 0, "Unexpected path.\n");
+
+    for (i = 0; i < ARRAY_SIZE(removebackslash_tests); i++)
+    {
+        const struct removebackslash_test *test = &removebackslash_tests[i];
+        char path[MAX_PATH];
+
+        MultiByteToWideChar(CP_ACP, 0, test->path, -1, pathW, ARRAY_SIZE(pathW));
+        hr = pPathCchRemoveBackslash(pathW, test->size);
+        ok(hr == test->hr, "%u: unexpected return value %#x.\n", i, hr);
+
+        WideCharToMultiByte(CP_ACP, 0, pathW, -1, path, ARRAY_SIZE(path), NULL, NULL);
+        ok(!strcmp(path, test->result), "%u: unexpected resulting path %s.\n", i, path);
+    }
+}
+
+static void test_PathCchRemoveBackslashEx(void)
+{
+    WCHAR pathW[MAX_PATH];
+    SIZE_T remaining;
+    unsigned int i;
+    HRESULT hr;
+    WCHAR *ptrW;
+
+    if (!pPathCchRemoveBackslashEx)
+    {
+        win_skip("PathCchRemoveBackslashEx() is not available.\n");
+        return;
+    }
+
+    pathW[0] = 0;
+    hr = pPathCchRemoveBackslashEx(0, 0, NULL, NULL);
+    ok(hr == E_INVALIDARG, "Unexpected hr %#x.\n", hr);
+    ok(pathW[0] == 0, "Unexpected path.\n");
+
+    pathW[0] = 0;
+    ptrW = (void *)0xdeadbeef;
+    remaining = 123;
+    hr = pPathCchRemoveBackslashEx(pathW, 1, &ptrW, &remaining);
+    ok(hr == S_FALSE, "Unexpected hr %#x.\n", hr);
+    ok(pathW[0] == 0, "Unexpected path.\n");
+    ok(ptrW == pathW, "Unexpected endptr %p.\n", ptrW);
+    ok(remaining == 1, "Unexpected remaining size.\n");
+
+    pathW[0] = 0;
+    hr = pPathCchRemoveBackslashEx(pathW, 2, NULL, NULL);
+    ok(hr == S_FALSE, "Unexpected hr %#x.\n", hr);
+    ok(pathW[0] == 0, "Unexpected path.\n");
+
+    for (i = 0; i < ARRAY_SIZE(removebackslash_tests); i++)
+    {
+        const struct removebackslash_test *test = &removebackslash_tests[i];
+        char path[MAX_PATH];
+
+        MultiByteToWideChar(CP_ACP, 0, test->path, -1, pathW, ARRAY_SIZE(pathW));
+        hr = pPathCchRemoveBackslashEx(pathW, test->size, NULL, NULL);
+        ok(hr == test->hr, "%u: unexpected return value %#x.\n", i, hr);
+
+        WideCharToMultiByte(CP_ACP, 0, pathW, -1, path, ARRAY_SIZE(path), NULL, NULL);
+        ok(!strcmp(path, test->result), "%u: unexpected resulting path %s.\n", i, path);
+
+        ptrW = (void *)0xdeadbeef;
+        remaining = 123;
+        MultiByteToWideChar(CP_ACP, 0, test->path, -1, pathW, ARRAY_SIZE(pathW));
+        hr = pPathCchRemoveBackslashEx(pathW, test->size, &ptrW, &remaining);
+        ok(hr == test->hr, "%u: unexpected return value %#x.\n", i, hr);
+        if (SUCCEEDED(hr))
+        {
+            ok(ptrW == (pathW + lstrlenW(pathW)), "%u: unexpected end pointer.\n", i);
+            ok(remaining == test->remaining, "%u: unexpected remaining buffer length.\n", i);
+        }
+        else
+        {
+            ok(ptrW == NULL, "%u: unexpecred end pointer.\n", i);
+            ok(remaining == 0, "%u: unexpected remaining buffer length.\n", i);
+        }
+    }
+}
+
 START_TEST(path)
 {
     HMODULE hmod = LoadLibraryA("kernelbase.dll");
@@ -252,8 +375,12 @@ START_TEST(path)
     pPathCchCombineEx = (void *)GetProcAddress(hmod, "PathCchCombineEx");
     pPathCchAddBackslash = (void *)GetProcAddress(hmod, "PathCchAddBackslash");
     pPathCchAddBackslashEx = (void *)GetProcAddress(hmod, "PathCchAddBackslashEx");
+    pPathCchRemoveBackslash = (void *)GetProcAddress(hmod, "PathCchRemoveBackslash");
+    pPathCchRemoveBackslashEx = (void *)GetProcAddress(hmod, "PathCchRemoveBackslashEx");
 
     test_PathCchCombineEx();
     test_PathCchAddBackslash();
     test_PathCchAddBackslashEx();
+    test_PathCchRemoveBackslash();
+    test_PathCchRemoveBackslashEx();
 }
diff --git a/include/pathcch.h b/include/pathcch.h
index 2b2aed4c8f9..c19ed1d9306 100644
--- a/include/pathcch.h
+++ b/include/pathcch.h
@@ -25,4 +25,6 @@
 
 HRESULT WINAPI PathCchAddBackslash(WCHAR *path, SIZE_T size);
 HRESULT WINAPI PathCchAddBackslashEx(WCHAR *path, SIZE_T size, WCHAR **end, SIZE_T *remaining);
+HRESULT WINAPI PathCchRemoveBackslash(WCHAR *path, SIZE_T size);
+HRESULT WINAPI PathCchRemoveBackslashEx(WCHAR *path, SIZE_T size, WCHAR **end, SIZE_T *remaining);
 HRESULT WINAPI PathCchCombineEx(WCHAR *out, SIZE_T size, const WCHAR *path1, const WCHAR *path2, DWORD flags);
-- 
2.18.0