[PATCH] kernelbase: Duplicate some path handling functions from shlwapi.

Nikolay Sivov nsivov at codeweavers.com
Tue May 14 05:30:35 CDT 2019


Signed-off-by: Nikolay Sivov <nsivov at codeweavers.com>
---
 dlls/kernelbase/kernelbase.spec |  54 +--
 dlls/kernelbase/path.c          | 753 ++++++++++++++++++++++++++++++++
 2 files changed, 780 insertions(+), 27 deletions(-)

diff --git a/dlls/kernelbase/kernelbase.spec b/dlls/kernelbase/kernelbase.spec
index 7151bbae3c..d1bcea5593 100644
--- a/dlls/kernelbase/kernelbase.spec
+++ b/dlls/kernelbase/kernelbase.spec
@@ -1020,16 +1020,16 @@
 # @ stub ParseApplicationUserModelId
 @ stdcall ParseURLA(str ptr) shlwapi.ParseURLA
 @ stdcall ParseURLW(wstr ptr) shlwapi.ParseURLW
-@ stdcall PathAddBackslashA(str) shlwapi.PathAddBackslashA
-@ stdcall PathAddBackslashW(wstr) shlwapi.PathAddBackslashW
-@ stdcall PathAddExtensionA(str str) shlwapi.PathAddExtensionA
-@ stdcall PathAddExtensionW(wstr wstr) shlwapi.PathAddExtensionW
+@ stdcall PathAddBackslashA(str)
+@ stdcall PathAddBackslashW(wstr)
+@ stdcall PathAddExtensionA(str str)
+@ stdcall PathAddExtensionW(wstr wstr)
 @ stdcall PathAllocCanonicalize(wstr long ptr)
 @ stdcall PathAllocCombine(wstr wstr long ptr)
-@ stdcall PathAppendA(str str) shlwapi.PathAppendA
-@ stdcall PathAppendW(wstr wstr) shlwapi.PathAppendW
-@ stdcall PathCanonicalizeA(ptr str) shlwapi.PathCanonicalizeA
-@ stdcall PathCanonicalizeW(ptr wstr) shlwapi.PathCanonicalizeW
+@ stdcall PathAppendA(str str)
+@ stdcall PathAppendW(wstr wstr)
+@ stdcall PathCanonicalizeA(ptr str)
+@ stdcall PathCanonicalizeW(ptr wstr)
 @ stdcall PathCchAddBackslash(wstr long)
 @ stdcall PathCchAddBackslashEx(wstr long ptr ptr)
 @ stdcall PathCchAddExtension(wstr long wstr)
@@ -1049,17 +1049,17 @@
 @ stdcall PathCchSkipRoot(wstr ptr)
 @ stdcall PathCchStripPrefix(wstr long)
 @ stdcall PathCchStripToRoot(wstr long)
-@ stdcall PathCombineA(ptr str str) shlwapi.PathCombineA
-@ stdcall PathCombineW(ptr wstr wstr) shlwapi.PathCombineW
-@ stdcall PathCommonPrefixA(str str ptr) shlwapi.PathCommonPrefixA
-@ stdcall PathCommonPrefixW(wstr wstr ptr) shlwapi.PathCommonPrefixW
+@ stdcall PathCombineA(ptr str str)
+@ stdcall PathCombineW(ptr wstr wstr)
+@ stdcall PathCommonPrefixA(str str ptr)
+@ stdcall PathCommonPrefixW(wstr wstr ptr)
 @ stdcall PathCreateFromUrlA(str ptr ptr long) shlwapi.PathCreateFromUrlA
 @ stdcall PathCreateFromUrlAlloc(wstr ptr long) shlwapi.PathCreateFromUrlAlloc
 @ stdcall PathCreateFromUrlW(wstr ptr ptr long) shlwapi.PathCreateFromUrlW
 @ stdcall PathFileExistsA(str) shlwapi.PathFileExistsA
 @ stdcall PathFileExistsW(wstr) shlwapi.PathFileExistsW
-@ stdcall PathFindExtensionA(str) shlwapi.PathFindExtensionA
-@ stdcall PathFindExtensionW(wstr) shlwapi.PathFindExtensionW
+@ stdcall PathFindExtensionA(str)
+@ stdcall PathFindExtensionW(wstr)
 @ stdcall PathFindFileNameA(str) shlwapi.PathFindFileNameA
 @ stdcall PathFindFileNameW(wstr) shlwapi.PathFindFileNameW
 @ stdcall PathFindNextComponentA(str) shlwapi.PathFindNextComponentA
@@ -1074,21 +1074,21 @@
 @ stdcall PathIsFileSpecW(wstr) shlwapi.PathIsFileSpecW
 @ stdcall PathIsLFNFileSpecA(str) shlwapi.PathIsLFNFileSpecA
 @ stdcall PathIsLFNFileSpecW(wstr) shlwapi.PathIsLFNFileSpecW
-@ stdcall PathIsPrefixA(str str) shlwapi.PathIsPrefixA
-@ stdcall PathIsPrefixW(wstr wstr) shlwapi.PathIsPrefixW
-@ stdcall PathIsRelativeA(str) shlwapi.PathIsRelativeA
-@ stdcall PathIsRelativeW(wstr) shlwapi.PathIsRelativeW
-@ stdcall PathIsRootA(str) shlwapi.PathIsRootA
-@ stdcall PathIsRootW(wstr) shlwapi.PathIsRootW
+@ stdcall PathIsPrefixA(str str)
+@ stdcall PathIsPrefixW(wstr wstr)
+@ stdcall PathIsRelativeA(str)
+@ stdcall PathIsRelativeW(wstr)
+@ stdcall PathIsRootA(str)
+@ stdcall PathIsRootW(wstr)
 @ stdcall PathIsSameRootA(str str) shlwapi.PathIsSameRootA
 @ stdcall PathIsSameRootW(wstr wstr) shlwapi.PathIsSameRootW
-@ stdcall PathIsUNCA(str) shlwapi.PathIsUNCA
+@ stdcall PathIsUNCA(str)
 @ stdcall PathIsUNCEx(wstr ptr)
 @ stdcall PathIsUNCServerA(str) shlwapi.PathIsUNCServerA
-@ stdcall PathIsUNCServerShareA(str) shlwapi.PathIsUNCServerShareA
-@ stdcall PathIsUNCServerShareW(wstr) shlwapi.PathIsUNCServerShareW
+@ stdcall PathIsUNCServerShareA(str)
+@ stdcall PathIsUNCServerShareW(wstr)
 @ stdcall PathIsUNCServerW(wstr) shlwapi.PathIsUNCServerW
-@ stdcall PathIsUNCW(wstr) shlwapi.PathIsUNCW
+@ stdcall PathIsUNCW(wstr)
 @ stdcall PathIsURLA(str) shlwapi.PathIsURLA
 @ stdcall PathIsURLW(wstr) shlwapi.PathIsURLW
 @ stdcall PathIsValidCharA(long long) shlwapi.PathIsValidCharA
@@ -1109,8 +1109,8 @@
 @ stdcall PathRemoveBlanksW(wstr) shlwapi.PathRemoveBlanksW
 @ stdcall PathRemoveExtensionA(str) shlwapi.PathRemoveExtensionA
 @ stdcall PathRemoveExtensionW(wstr) shlwapi.PathRemoveExtensionW
-@ stdcall PathRemoveFileSpecA(str) shlwapi.PathRemoveFileSpecA
-@ stdcall PathRemoveFileSpecW(wstr) shlwapi.PathRemoveFileSpecW
+@ stdcall PathRemoveFileSpecA(str)
+@ stdcall PathRemoveFileSpecW(wstr)
 @ stdcall PathRenameExtensionA(str str) shlwapi.PathRenameExtensionA
 @ stdcall PathRenameExtensionW(wstr wstr) shlwapi.PathRenameExtensionW
 @ stdcall PathSearchAndQualifyA(str ptr long) shlwapi.PathSearchAndQualifyA
@@ -1120,7 +1120,7 @@
 @ stdcall PathStripPathA(str) shlwapi.PathStripPathA
 @ stdcall PathStripPathW(wstr) shlwapi.PathStripPathW
 @ stdcall PathStripToRootA(str) shlwapi.PathStripToRootA
-@ stdcall PathStripToRootW(wstr) shlwapi.PathStripToRootW
+@ stdcall PathStripToRootW(wstr)
 @ stdcall PathUnExpandEnvStringsA(str ptr long) shlwapi.PathUnExpandEnvStringsA
 @ stdcall PathUnExpandEnvStringsW(wstr ptr long) shlwapi.PathUnExpandEnvStringsW
 @ stdcall PathUnquoteSpacesA(str) shlwapi.PathUnquoteSpacesA
diff --git a/dlls/kernelbase/path.c b/dlls/kernelbase/path.c
index 5331f373c2..a72ff8e694 100644
--- a/dlls/kernelbase/path.c
+++ b/dlls/kernelbase/path.c
@@ -29,6 +29,13 @@
 
 WINE_DEFAULT_DEBUG_CHANNEL(path);
 
+char *char_next(const char *ptr)
+{
+    if (!*ptr) return (LPSTR)ptr;
+    if (IsDBCSLeadByte( ptr[0] ) && ptr[1]) return (LPSTR)(ptr + 2);
+    return (LPSTR)(ptr + 1);
+}
+
 static SIZE_T strnlenW(const WCHAR *string, SIZE_T maxlen)
 {
     SIZE_T i;
@@ -865,3 +872,749 @@ BOOL WINAPI PathIsUNCEx(const WCHAR *path, const WCHAR **server)
     if (server) *server = result;
     return !!result;
 }
+
+BOOL WINAPI PathIsUNCA(const char *path)
+{
+    TRACE("%s\n", wine_dbgstr_a(path));
+
+    return path && (path[0] == '\\') && (path[1] == '\\');
+}
+
+BOOL WINAPI PathIsUNCW(const WCHAR *path)
+{
+    TRACE("%s\n", wine_dbgstr_w(path));
+
+    return path && (path[0] == '\\') && (path[1] == '\\');
+}
+
+BOOL WINAPI PathIsRelativeA(const char *path)
+{
+    TRACE("%s\n", wine_dbgstr_a(path));
+
+    if (!path || !*path || IsDBCSLeadByte(*path))
+        return TRUE;
+
+    return !(*path == '\\' || (*path && path[1] == ':'));
+}
+
+BOOL WINAPI PathIsRelativeW(const WCHAR *path)
+{
+    TRACE("%s\n", wine_dbgstr_w(path));
+
+    if (!path || !*path)
+        return TRUE;
+
+    return !(*path == '\\' || (*path && path[1] == ':'));
+}
+
+BOOL WINAPI PathIsUNCServerShareA(const char *path)
+{
+    BOOL seen_slash = FALSE;
+
+    TRACE("%s\n", wine_dbgstr_a(path));
+
+    if (path && *path++ == '\\' && *path++ == '\\')
+    {
+        while (*path)
+        {
+            if (*path == '\\')
+            {
+                if (seen_slash)
+                    return FALSE;
+                seen_slash = TRUE;
+            }
+
+            path = char_next(path);
+        }
+    }
+
+    return seen_slash;
+}
+
+BOOL WINAPI PathIsUNCServerShareW(const WCHAR *path)
+{
+    BOOL seen_slash = FALSE;
+
+    TRACE("%s\n", wine_dbgstr_w(path));
+
+    if (path && *path++ == '\\' && *path++ == '\\')
+    {
+        while (*path)
+        {
+            if (*path == '\\')
+            {
+                if (seen_slash)
+                    return FALSE;
+                seen_slash = TRUE;
+            }
+
+            path++;
+        }
+    }
+
+    return seen_slash;
+}
+
+BOOL WINAPI PathIsRootA(const char *path)
+{
+    TRACE("%s\n", wine_dbgstr_a(path));
+
+    if (!path || !*path)
+        return FALSE;
+
+    if (*path == '\\')
+    {
+        if (!path[1])
+            return TRUE; /* \ */
+        else if (path[1] == '\\')
+        {
+            BOOL seen_slash = FALSE;
+            path += 2;
+
+            /* Check for UNC root path */
+            while (*path)
+            {
+                if (*path == '\\')
+                {
+                    if (seen_slash)
+                        return FALSE;
+                    seen_slash = TRUE;
+                }
+
+                path = char_next(path);
+            }
+
+            return TRUE;
+        }
+    }
+    else if (path[1] == ':' && path[2] == '\\' && path[3] == '\0')
+        return TRUE; /* X:\ */
+
+    return FALSE;
+}
+
+BOOL WINAPI PathIsRootW(const WCHAR *path)
+{
+    TRACE("%s\n", wine_dbgstr_w(path));
+
+    if (!path || !*path)
+        return FALSE;
+
+    if (*path == '\\')
+    {
+        if (!path[1])
+            return TRUE; /* \ */
+        else if (path[1] == '\\')
+        {
+            BOOL seen_slash = FALSE;
+
+            path += 2;
+            /* Check for UNC root path */
+            while (*path)
+            {
+                if (*path == '\\')
+                {
+                    if (seen_slash)
+                        return FALSE;
+                    seen_slash = TRUE;
+                }
+                path++;
+            }
+
+            return TRUE;
+        }
+    }
+    else if (path[1] == ':' && path[2] == '\\' && path[3] == '\0')
+        return TRUE; /* X:\ */
+
+    return FALSE;
+}
+
+BOOL WINAPI PathRemoveFileSpecA(char *path)
+{
+    char *filespec = path;
+    BOOL modified = FALSE;
+
+    TRACE("%s\n", wine_dbgstr_a(path));
+
+    if (!path)
+        return FALSE;
+
+    /* Skip directory or UNC path */
+    if (*path == '\\')
+        filespec = ++path;
+    if (*path == '\\')
+        filespec = ++path;
+
+    while (*path)
+    {
+        if (*path == '\\')
+            filespec = path; /* Skip dir */
+        else if (*path == ':')
+        {
+            filespec = ++path; /* Skip drive */
+            if (*path == '\\')
+                filespec++;
+        }
+        if (!(path = char_next(path)))
+            break;
+    }
+
+    if (*filespec)
+    {
+        *filespec = '\0';
+        modified = TRUE;
+    }
+
+    return modified;
+}
+
+BOOL WINAPI PathRemoveFileSpecW(WCHAR *path)
+{
+    WCHAR *filespec = path;
+    BOOL modified = FALSE;
+
+    TRACE("%s\n", wine_dbgstr_w(path));
+
+    if (!path)
+        return FALSE;
+
+    /* Skip directory or UNC path */
+    if (*path == '\\')
+        filespec = ++path;
+    if (*path == '\\')
+        filespec = ++path;
+
+    while (*path)
+    {
+        if (*path == '\\')
+            filespec = path; /* Skip dir */
+        else if (*path == ':')
+        {
+            filespec = ++path; /* Skip drive */
+            if (*path == '\\')
+                filespec++;
+        }
+
+        path++;
+    }
+
+    if (*filespec)
+    {
+        *filespec = '\0';
+        modified = TRUE;
+    }
+
+    return modified;
+}
+
+BOOL WINAPI PathStripToRootA(char *path)
+{
+    TRACE("%s\n", wine_dbgstr_a(path));
+
+    if (!path)
+        return FALSE;
+
+    while (!PathIsRootA(path))
+        if (!PathRemoveFileSpecA(path))
+            return FALSE;
+
+    return TRUE;
+}
+
+BOOL WINAPI PathStripToRootW(WCHAR *path)
+{
+    TRACE("%s\n", wine_dbgstr_w(path));
+
+    if (!path)
+        return FALSE;
+
+    while (!PathIsRootW(path))
+        if (!PathRemoveFileSpecW(path))
+            return FALSE;
+
+    return TRUE;
+}
+
+LPSTR WINAPI PathAddBackslashA(char *path)
+{
+    unsigned int len;
+    char *prev = path;
+
+    TRACE("%s\n", wine_dbgstr_a(path));
+
+    if (!path || (len = strlen(path)) >= MAX_PATH)
+        return NULL;
+
+    if (len)
+    {
+        do
+        {
+            path = char_next(prev);
+            if (*path)
+            prev = path;
+        } while (*path);
+
+        if (*prev != '\\')
+        {
+            *path++ = '\\';
+            *path = '\0';
+        }
+    }
+
+    return path;
+}
+
+LPWSTR WINAPI PathAddBackslashW(WCHAR *path)
+{
+    unsigned int len;
+
+    TRACE("%s\n", wine_dbgstr_w(path));
+
+    if (!path || (len = strlenW(path)) >= MAX_PATH)
+        return NULL;
+
+    if (len)
+    {
+        path += len;
+        if (path[-1] != '\\')
+        {
+            *path++ = '\\';
+            *path = '\0';
+        }
+    }
+
+    return path;
+}
+
+LPSTR WINAPI PathFindExtensionA(const char *path)
+{
+    const char *lastpoint = NULL;
+
+    TRACE("%s\n", wine_dbgstr_a(path));
+
+    if (path)
+    {
+        while (*path)
+        {
+            if (*path == '\\' || *path == ' ')
+                lastpoint = NULL;
+            else if (*path == '.')
+                lastpoint = path;
+            path = char_next(path);
+        }
+    }
+
+    return (LPSTR)(lastpoint ? lastpoint : path);
+}
+
+LPWSTR WINAPI PathFindExtensionW(const WCHAR *path)
+{
+    const WCHAR *lastpoint = NULL;
+
+    TRACE("%s\n", wine_dbgstr_w(path));
+
+    if (path)
+    {
+        while (*path)
+        {
+            if (*path == '\\' || *path == ' ')
+                lastpoint = NULL;
+            else if (*path == '.')
+                lastpoint = path;
+            path++;
+        }
+    }
+
+    return (LPWSTR)(lastpoint ? lastpoint : path);
+}
+
+BOOL WINAPI PathAddExtensionA(char *path, const char *ext)
+{
+    unsigned int len;
+
+    TRACE("%s, %s\n", wine_dbgstr_a(path), wine_dbgstr_a(ext));
+
+    if (!path || !ext || *(PathFindExtensionA(path)))
+        return FALSE;
+
+    len = strlen(path);
+    if (len + strlen(ext) >= MAX_PATH)
+        return FALSE;
+
+    strcpy(path + len, ext);
+    return TRUE;
+}
+
+BOOL WINAPI PathAddExtensionW(WCHAR *path, const WCHAR *ext)
+{
+    unsigned int len;
+
+    TRACE("%s, %s\n", wine_dbgstr_w(path), wine_dbgstr_w(ext));
+
+    if (!path || !ext || *(PathFindExtensionW(path)))
+        return FALSE;
+
+    len = strlenW(path);
+    if (len + strlenW(ext) >= MAX_PATH)
+        return FALSE;
+
+    strcpyW(path + len, ext);
+    return TRUE;
+}
+
+BOOL WINAPI PathCanonicalizeW(WCHAR *buffer, const WCHAR *path)
+{
+    const WCHAR *src = path;
+    WCHAR *dst = buffer;
+
+    TRACE("%p, %s\n", buffer, wine_dbgstr_w(path));
+
+    if (dst)
+        *dst = '\0';
+
+    if (!dst || !path)
+    {
+        SetLastError(ERROR_INVALID_PARAMETER);
+        return FALSE;
+    }
+
+    if (!*path)
+    {
+        *buffer++ = '\\';
+        *buffer = '\0';
+        return TRUE;
+    }
+
+    /* Copy path root */
+    if (*src == '\\')
+    {
+        *dst++ = *src++;
+    }
+    else if (*src && src[1] == ':')
+    {
+        /* X:\ */
+        *dst++ = *src++;
+        *dst++ = *src++;
+        if (*src == '\\')
+            *dst++ = *src++;
+    }
+
+    /* Canonicalize the rest of the path */
+    while (*src)
+    {
+        if (*src == '.')
+        {
+            if (src[1] == '\\' && (src == path || src[-1] == '\\' || src[-1] == ':'))
+            {
+                src += 2; /* Skip .\ */
+            }
+            else if (src[1] == '.' && (dst == buffer || dst[-1] == '\\'))
+            {
+                /* \.. backs up a directory, over the root if it has no \ following X:.
+                 * .. is ignored if it would remove a UNC server name or initial \\
+                 */
+                if (dst != buffer)
+                {
+                    *dst = '\0'; /* Allow PathIsUNCServerShareA test on lpszBuf */
+                    if (dst > buffer + 1 && dst[-1] == '\\' && (dst[-2] != '\\' || dst > buffer + 2))
+                    {
+                        if (dst[-2] == ':' && (dst > buffer + 3 || dst[-3] == ':'))
+                        {
+                            dst -= 2;
+                            while (dst > buffer && *dst != '\\')
+                                dst--;
+                            if (*dst == '\\')
+                                dst++; /* Reset to last '\' */
+                            else
+                                dst = buffer; /* Start path again from new root */
+                        }
+                        else if (dst[-2] != ':' && !PathIsUNCServerShareW(buffer))
+                            dst -= 2;
+                    }
+                    while (dst > buffer && *dst != '\\')
+                        dst--;
+                    if (dst == buffer)
+                    {
+                        *dst++ = '\\';
+                        src++;
+                    }
+                }
+                src += 2; /* Skip .. in src path */
+            }
+            else
+                *dst++ = *src++;
+        }
+        else
+            *dst++ = *src++;
+    }
+
+    /* Append \ to naked drive specs */
+    if (dst - buffer == 2 && dst[-1] == ':')
+        *dst++ = '\\';
+    *dst++ = '\0';
+    return TRUE;
+}
+
+BOOL WINAPI PathCanonicalizeA(char *buffer, const char *path)
+{
+    WCHAR pathW[MAX_PATH], bufferW[MAX_PATH];
+    BOOL ret;
+    int len;
+
+    TRACE("%p, %s\n", buffer, wine_dbgstr_a(path));
+
+    if (buffer)
+        *buffer = '\0';
+
+    if (!buffer || !path)
+    {
+        SetLastError(ERROR_INVALID_PARAMETER);
+        return FALSE;
+    }
+
+    len = MultiByteToWideChar(CP_ACP, 0, path, -1, pathW, ARRAY_SIZE(pathW));
+    if (!len)
+        return FALSE;
+
+    ret = PathCanonicalizeW(bufferW, pathW);
+    WideCharToMultiByte(CP_ACP, 0, bufferW, -1, buffer, MAX_PATH, 0, 0);
+
+    return ret;
+}
+
+WCHAR * WINAPI PathCombineW(WCHAR *dst, const WCHAR *dir, const WCHAR *file)
+{
+    BOOL use_both = FALSE, strip = FALSE;
+    WCHAR tmp[MAX_PATH];
+
+    TRACE("%p, %s, %s\n", dst, wine_dbgstr_w(dir), wine_dbgstr_w(file));
+
+    /* Invalid parameters */
+    if (!dst)
+        return NULL;
+
+    if (!dir && !file)
+    {
+        dst[0] = 0;
+        return NULL;
+    }
+
+    if ((!file || !*file) && dir)
+    {
+        /* Use dir only */
+        lstrcpynW(tmp, dir, ARRAY_SIZE(tmp));
+    }
+    else if (!dir || !*dir || !PathIsRelativeW(file))
+    {
+        if (!dir || !*dir || *file != '\\' || PathIsUNCW(file))
+        {
+            /* Use file only */
+            lstrcpynW(tmp, file, ARRAY_SIZE(tmp));
+        }
+        else
+        {
+            use_both = TRUE;
+            strip = TRUE;
+        }
+    }
+    else
+        use_both = TRUE;
+
+    if (use_both)
+    {
+        lstrcpynW(tmp, dir, ARRAY_SIZE(tmp));
+        if (strip)
+        {
+            PathStripToRootW(tmp);
+            file++; /* Skip '\' */
+        }
+
+        if (!PathAddBackslashW(tmp) || strlenW(tmp) + strlenW(file) >= MAX_PATH)
+        {
+            dst[0] = 0;
+            return NULL;
+        }
+
+        strcatW(tmp, file);
+    }
+
+    PathCanonicalizeW(dst, tmp);
+    return dst;
+}
+
+LPSTR WINAPI PathCombineA(char *dst, const char *dir, const char *file)
+{
+    WCHAR dstW[MAX_PATH], dirW[MAX_PATH], fileW[MAX_PATH];
+
+    TRACE("%p, %s, %s\n", dst, wine_dbgstr_a(dir), wine_dbgstr_a(file));
+
+    /* Invalid parameters */
+    if (!dst)
+        return NULL;
+
+    dst[0] = 0;
+
+    if (!dir && !file)
+        return NULL;
+
+    if (dir && !MultiByteToWideChar(CP_ACP, 0, dir, -1, dirW, ARRAY_SIZE(dirW)))
+        return NULL;
+
+    if (file && !MultiByteToWideChar(CP_ACP, 0, file, -1, fileW, ARRAY_SIZE(fileW)))
+        return NULL;
+
+    if (PathCombineW(dstW, dir ? dirW : NULL, file ? fileW : NULL))
+        if (WideCharToMultiByte(CP_ACP, 0, dstW, -1, dst, MAX_PATH, 0, 0))
+            return dst;
+
+    return NULL;
+}
+
+BOOL WINAPI PathAppendA(char *path, const char *append)
+{
+    TRACE("%s, %s\n", wine_dbgstr_a(path), wine_dbgstr_a(append));
+
+    if (path && append)
+    {
+        if (!PathIsUNCA(append))
+            while (*append == '\\')
+                append++;
+
+        if (PathCombineA(path, path, append))
+            return TRUE;
+    }
+
+    return FALSE;
+}
+
+BOOL WINAPI PathAppendW(WCHAR *path, const WCHAR *append)
+{
+    TRACE("%s, %s\n", wine_dbgstr_w(path), wine_dbgstr_w(append));
+
+    if (path && append)
+    {
+        if (!PathIsUNCW(append))
+            while (*append == '\\')
+                append++;
+
+        if (PathCombineW(path, path, append))
+            return TRUE;
+    }
+
+    return FALSE;
+}
+
+int WINAPI PathCommonPrefixA(const char *file1, const char *file2, char *path)
+{
+    const char *iter1 = file1;
+    const char *iter2 = file2;
+    unsigned int len = 0;
+
+    TRACE("%s, %s, %p.\n", wine_dbgstr_a(file1), wine_dbgstr_a(file2), path);
+
+    if (path)
+        *path = '\0';
+
+    if (!file1 || !file2)
+        return 0;
+
+    /* Handle roots first */
+    if (PathIsUNCA(file1))
+    {
+        if (!PathIsUNCA(file2))
+            return 0;
+        iter1 += 2;
+        iter2 += 2;
+    }
+    else if (PathIsUNCA(file2))
+        return 0;
+
+    for (;;)
+    {
+        /* Update len */
+        if ((!*iter1 || *iter1 == '\\') && (!*iter2 || *iter2 == '\\'))
+            len = iter1 - file1; /* Common to this point */
+
+        if (!*iter1 || (tolower(*iter1) != tolower(*iter2)))
+            break; /* Strings differ at this point */
+
+        iter1++;
+        iter2++;
+    }
+
+    if (len == 2)
+        len++; /* Feature/Bug compatible with Win32 */
+
+    if (len && path)
+    {
+        memcpy(path, file1, len);
+        path[len] = '\0';
+    }
+
+    return len;
+}
+
+int WINAPI PathCommonPrefixW(const WCHAR *file1, const WCHAR *file2, WCHAR *path)
+{
+    const WCHAR *iter1 = file1;
+    const WCHAR *iter2 = file2;
+    unsigned int len = 0;
+
+    TRACE("%s, %s, %p\n", wine_dbgstr_w(file1), wine_dbgstr_w(file2), path);
+
+    if (path)
+        *path = '\0';
+
+    if (!file1 || !file2)
+        return 0;
+
+    /* Handle roots first */
+    if (PathIsUNCW(file1))
+    {
+        if (!PathIsUNCW(file2))
+            return 0;
+        iter1 += 2;
+        iter2 += 2;
+    }
+    else if (PathIsUNCW(file2))
+      return 0;
+
+    for (;;)
+    {
+        /* Update len */
+        if ((!*iter1 || *iter1 == '\\') && (!*iter2 || *iter2 == '\\'))
+            len = iter1 - file1; /* Common to this point */
+
+        if (!*iter1 || (tolowerW(*iter1) != tolowerW(*iter2)))
+            break; /* Strings differ at this point */
+
+        iter1++;
+        iter2++;
+    }
+
+    if (len == 2)
+        len++; /* Feature/Bug compatible with Win32 */
+
+    if (len && path)
+    {
+        memcpy(path, file1, len * sizeof(WCHAR));
+        path[len] = '\0';
+    }
+
+    return len;
+}
+
+BOOL WINAPI PathIsPrefixA(const char *prefix, const char *path)
+{
+    TRACE("%s, %s\n", wine_dbgstr_a(prefix), wine_dbgstr_a(path));
+
+    return prefix && path && PathCommonPrefixA(path, prefix, NULL) == (int)strlen(prefix);
+}
+
+BOOL WINAPI PathIsPrefixW(const WCHAR *prefix, const WCHAR *path)
+{
+    TRACE("%s, %s\n", wine_dbgstr_w(prefix), wine_dbgstr_w(path));
+
+    return prefix && path && PathCommonPrefixW(path, prefix, NULL) == (int)strlenW(prefix);
+}
-- 
2.20.1




More information about the wine-devel mailing list