I doubt shell32 function on windows does this check, also original windows shell32 function is a lot more complicated and longer, I've compared the results between real and my code and they are correct.
I wrote a small test for this function showing that your implementation is almost correct. It failes in two cases:
1. If a path ends with '' and is compared with one that doesn't, then it's length is less than the other one, but both are the same paths. 2. If a path doesn't end with '' and is compared with a longer one that has the same first len chars, but is not the same, ex: c:\test c:\test123
Changelog: Added test for PathIsEqualOrSubFolder
Index: dlls/shell32/tests/shellpath.c =================================================================== RCS file: /home/wine/wine/dlls/shell32/tests/shellpath.c,v retrieving revision 1.6 diff -u -p -r1.6 shellpath.c --- dlls/shell32/tests/shellpath.c 10 Feb 2005 19:19:35 -0000 1.6 +++ dlls/shell32/tests/shellpath.c 28 Feb 2005 21:19:31 -0000 @@ -871,6 +871,55 @@ static void testNonExistentPath(void) userShellFolders); }
+typedef BOOL (WINAPI*fnPathIsEqualOrSubFolder)(LPCWSTR path1, LPCWSTR path2); + +static void test_PathIsEqualOrSubFolder() +{ + fnPathIsEqualOrSubFolder pPathIsEqualOrSubFolder; + BOOL ret; + static const WCHAR path1[] = {'c',':','\','t','e','s','t',0}; + static const WCHAR path2[] = {'c',':','\','t','e','s','t','\','t','e','s','t',0}; + static const WCHAR path3[] = {'c',':','\','t','e','s','t','1','2','3',0}; + static const WCHAR path4[] = {'c',':','\','t','e','s','t','\',0}; + static const WCHAR path5[] = {'c',':','\','t','e','s','t','\','t','e','s','t','2', + '\','.','.','\','t','e','s','t',0}; + + pPathIsEqualOrSubFolder = GetProcAddress(hShell32, (LPSTR)755); + if(!pPathIsEqualOrSubFolder) + return; + +#define TEST_PATH(p1, p2, r) \ + ret = pPathIsEqualOrSubFolder(p1, p2); \ + ok(ret == r, "PathIsEqualOrSubFolder(" #p1 " " #p2 ") failed, expected: %x\n", r); + + TEST_PATH(path1, path1, TRUE); + TEST_PATH(path1, path2, TRUE); + TEST_PATH(path1, path3, FALSE); + TEST_PATH(path1, path4, TRUE); + TEST_PATH(path2, path1, FALSE); + TEST_PATH(path2, path2, TRUE); + TEST_PATH(path2, path3, FALSE); + TEST_PATH(path2, path4, FALSE); + TEST_PATH(path3, path1, FALSE); + TEST_PATH(path3, path2, FALSE); + TEST_PATH(path3, path3, TRUE); + TEST_PATH(path3, path4, FALSE); + TEST_PATH(path4, path1, TRUE); + TEST_PATH(path4, path2, TRUE); + TEST_PATH(path4, path3, FALSE); + TEST_PATH(path4, path4, TRUE); + TEST_PATH(path5, path1, FALSE); + TEST_PATH(path5, path2, FALSE); + TEST_PATH(path5, path3, FALSE); + TEST_PATH(path5, path4, FALSE); + TEST_PATH(path5, path5, TRUE); + TEST_PATH(NULL, path1, FALSE); + TEST_PATH(path1, NULL, FALSE); + TEST_PATH(NULL, NULL, FALSE); + +#undef TEST_PATH +} + START_TEST(shellpath) { if (!init()) return; @@ -892,5 +941,6 @@ START_TEST(shellpath) testWinDir(); testSystemDir(); testNonExistentPath(); + test_PathIsEqualOrSubFolder(); } }
-----BEGIN PGP SIGNED MESSAGE----- Hash: SHA1
Changelog: ~ Fixed c:\test vs c:\test123
I really can implement it as m$ does but I think is very crazy to go around some dll, just to make a subpath match. Original m$ code calls: SHGetFolderPath, PathRemoveBackslash, PathCommonPrefix :)
- -- Gianluigi Tiesi sherpya@netfarm.it EDP Project Leader Netfarm S.r.l. - http://www.netfarm.it/ Free Software: http://oss.netfarm.it/
Index: shell32.spec =================================================================== RCS file: /home/wine/wine/dlls/shell32/shell32.spec,v retrieving revision 1.96 diff -u -r1.96 shell32.spec --- shell32.spec 23 Feb 2005 15:41:14 -0000 1.96 +++ shell32.spec 1 Mar 2005 05:35:01 -0000 @@ -250,6 +250,7 @@ # >= NT5 714 stdcall @(ptr) SHELL32_714 # PathIsTemporaryW 730 stdcall -noname RestartDialogEx(long wstr long long) + 755 stdcall -noname PathIsEqualOrSubFolder(wstr wstr)
1217 stub FOOBAR1217 # no joke! This is the real name!!
@@ -456,3 +457,4 @@ # _WIN32_IE >= 0x600 @ stdcall SHDefExtractIconA(str long long ptr ptr long) @ stdcall SHDefExtractIconW(wstr long long ptr ptr long) +@ stub SHCreateShellItem Index: shellpath.c =================================================================== RCS file: /home/wine/wine/dlls/shell32/shellpath.c,v retrieving revision 1.98 diff -u -r1.98 shellpath.c --- shellpath.c 14 Feb 2005 11:07:55 -0000 1.98 +++ shellpath.c 1 Mar 2005 05:35:01 -0000 @@ -411,6 +411,28 @@ }
/************************************************************************* + * PathIsEqualOrSubFolder [SHELL32.755] + */ +BOOL WINAPI PathIsEqualOrSubFolder(LPCWSTR path1, LPCWSTR path2) +{ + int i; + int len; + if (!path1 || !path2) return FALSE; + + len = strlenW(path1); + if (len > strlenW(path2)) return FALSE; + + for (i=0; i < len; i++) + if (tolowerW(path1[i]) != tolowerW(path2[i])) return FALSE; + + /* a trailing backslash in path1, + * makes path2 reach char just after the backslash */ + if (path2[i-1] == '\') return TRUE; + + return (!path2[i] || (path2[i] == '\')); +} + +/************************************************************************* * PathFileExists [SHELL32.45] */ BOOL WINAPI PathFileExistsAW (LPCVOID lpszPath) Index: undocshell.h =================================================================== RCS file: /home/wine/wine/dlls/shell32/undocshell.h,v retrieving revision 1.28 diff -u -r1.28 undocshell.h --- undocshell.h 13 Dec 2004 21:19:01 -0000 1.28 +++ undocshell.h 1 Mar 2005 05:35:01 -0000 @@ -410,6 +410,8 @@
BOOL WINAPI PathIsExeAW(LPCVOID lpszPath);
+BOOL WINAPI PathIsEqualOrSubFolder(LPCWSTR path1, LPCWSTR path2); + BOOL WINAPI PathIsDirectoryAW(LPCVOID lpszPath);
BOOL WINAPI PathFileExistsAW(LPCVOID lpszPath);
Gianluigi Tiesi wrote:
-----BEGIN PGP SIGNED MESSAGE----- Hash: SHA1
Changelog: ~ Fixed c:\test vs c:\test123
I really can implement it as m$ does but I think is very crazy to go around some dll, just to make a subpath match. Original m$ code calls: SHGetFolderPath, PathRemoveBackslash, PathCommonPrefix :)
Gianluigi Tiesi sherpya@netfarm.it EDP Project Leader Netfarm S.r.l. - http://www.netfarm.it/ Free Software: http://oss.netfarm.it/
-----BEGIN PGP SIGNATURE----- Version: GnuPG v1.2.4 (MingW32) Comment: Using GnuPG with Thunderbird - http://enigmail.mozdev.org
iD8DBQFCJADk3UE5cRfnO04RApbDAKCwswQyVtxe+xCGaDFrEISQK/3UyACfXWtC sFS2cEizLygq+D5F++L9duA= =5K/d -----END PGP SIGNATURE-----
Index: shell32.spec
RCS file: /home/wine/wine/dlls/shell32/shell32.spec,v retrieving revision 1.96 diff -u -r1.96 shell32.spec --- shell32.spec 23 Feb 2005 15:41:14 -0000 1.96 +++ shell32.spec 1 Mar 2005 05:35:01 -0000 @@ -250,6 +250,7 @@ # >= NT5 714 stdcall @(ptr) SHELL32_714 # PathIsTemporaryW 730 stdcall -noname RestartDialogEx(long wstr long long)
- 755 stdcall -noname PathIsEqualOrSubFolder(wstr wstr)
1217 stub FOOBAR1217 # no joke! This is the real name!!
@@ -456,3 +457,4 @@ # _WIN32_IE >= 0x600 @ stdcall SHDefExtractIconA(str long long ptr ptr long) @ stdcall SHDefExtractIconW(wstr long long ptr ptr long) +@ stub SHCreateShellItem Index: shellpath.c =================================================================== RCS file: /home/wine/wine/dlls/shell32/shellpath.c,v retrieving revision 1.98 diff -u -r1.98 shellpath.c --- shellpath.c 14 Feb 2005 11:07:55 -0000 1.98 +++ shellpath.c 1 Mar 2005 05:35:01 -0000 @@ -411,6 +411,28 @@ }
/*************************************************************************
- PathIsEqualOrSubFolder [SHELL32.755]
- */
+BOOL WINAPI PathIsEqualOrSubFolder(LPCWSTR path1, LPCWSTR path2) +{
- int i;
- int len;
- if (!path1 || !path2) return FALSE;
- len = strlenW(path1);
- if (len > strlenW(path2)) return FALSE;
- for (i=0; i < len; i++)
if (tolowerW(path1[i]) != tolowerW(path2[i])) return FALSE;
- /* a trailing backslash in path1,
* makes path2 reach char just after the backslash */
- if (path2[i-1] == '\') return TRUE;
- return (!path2[i] || (path2[i] == '\'));
+}
+/*************************************************************************
If path1 is path2 with an ending slash (like path1="C:\windows" and path2="C:\windows", the test: if (len > strlenW(path2)) return FALSE; will be true and the function will (incorrectly?) return FALSE; This line: if (path2[i-1] == '\') return TRUE; will never be executed ?
Sorry if I'm misreading...
HTH,
Joris