Signed-off-by: Nikolay Sivov nsivov@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); +}