1 /*
2  * Copyright 2017 Katayama Hirofumi MZ
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Lesser General Public
6  * License as published by the Free Software Foundation; either
7  * version 2.1 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Lesser General Public License for more details.
13  *
14  * You should have received a copy of the GNU Lesser General Public
15  * License along with this library; if not, write to the Free Software
16  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
17  */
18 
19 #include <apitest.h>
20 #include <shlwapi.h>
21 #include <assert.h>
22 
23 #include <pseh/pseh2.h>
24 
25 #define EF_FULLPATH     1
26 #define EF_TESTDATA     2
27 #define EF_WIN_DIR      4
28 #define EF_SYS_DIR      8
29 #define EF_TYPE_MASK    0xF
30 
31 #define EF_NAME_ONLY    16
32 
33 typedef struct ENTRY
34 {
35     INT         EntryNumber;    /* # */
36     INT         Ret;
37     DWORD       Error;
38     UINT        EF_;
39     LPCWSTR     NameBefore;
40     LPCWSTR     NameExpected;
41     LPCWSTR     PathExpected;
42     LPWSTR *    Dirs;
43 } ENTRY;
44 
45 #define BEEF        0xBEEF      /* Error Code 48879 */
46 #define DEAD        0xDEAD      /* Error Code 57005 */
47 #define IGNORE_ERR  0x7F7F7F7F  /* Ignore Error Code */
48 #define RAISED      9999        /* exception raised */
49 
50 static WCHAR    s_TestDataPath[MAX_PATH];
51 static LPWSTR   s_Dirs[2];
52 static WCHAR    s_WinDir[MAX_PATH];
53 static WCHAR    s_SysDir[MAX_PATH];
54 static WCHAR    s_FontsDir[MAX_PATH];
55 static WCHAR    s_NotepadPath[MAX_PATH];
56 
57 static const ENTRY s_Entries[] =
58 {
59     /* NULL or empty path */
60     { 0, 0, BEEF, EF_FULLPATH, NULL, NULL },
61     { 1, 1, BEEF, EF_FULLPATH, L"", s_SysDir },
62     /* path without dirs in Windows */
63     { 2, 0, BEEF, EF_WIN_DIR, L"", L"" },
64     { 3, 0, BEEF, EF_WIN_DIR, L"Fonts", L"Fonts" },
65     { 4, 0, BEEF, EF_WIN_DIR, L"notepad", L"notepad" },
66     { 5, 0, BEEF, EF_WIN_DIR, L"notepad.exe", L"notepad.exe" },
67     { 6, 0, BEEF, EF_WIN_DIR, L"system32", L"system32" },
68     { 7, 0, BEEF, EF_WIN_DIR, L"invalid name", L"invalid name" },
69     /* path with dirs in Windows */
70     { 8, 0, BEEF, EF_WIN_DIR, L"", L"", NULL, s_Dirs },
71     { 9, 0, BEEF, EF_WIN_DIR, L"Fonts", L"Fonts", NULL, s_Dirs },
72     { 10, 0, BEEF, EF_WIN_DIR, L"notepad", L"notepad", NULL, s_Dirs },
73     { 11, 0, BEEF, EF_WIN_DIR, L"notepad.exe", L"notepad.exe", NULL, s_Dirs },
74     { 12, 0, BEEF, EF_WIN_DIR, L"system32", L"system32", NULL, s_Dirs },
75     { 13, 0, BEEF, EF_WIN_DIR, L"invalid name", L"invalid name", NULL, s_Dirs },
76     /* name only without dirs in Windows */
77     { 14, 1, BEEF, EF_WIN_DIR | EF_NAME_ONLY, L"", L"system32" },
78     { 15, 1, ERROR_FILE_NOT_FOUND, EF_WIN_DIR | EF_NAME_ONLY, L"Fonts", L"Fonts" },
79     { 16, 0, ERROR_FILE_NOT_FOUND, EF_WIN_DIR | EF_NAME_ONLY, L"notepad" },
80     { 17, 1, BEEF, EF_WIN_DIR | EF_NAME_ONLY, L"notepad.exe", NULL, s_NotepadPath },
81     { 18, 1, ERROR_FILE_NOT_FOUND, EF_WIN_DIR | EF_NAME_ONLY, L"system32" },
82     { 19, 0, ERROR_FILE_NOT_FOUND, EF_WIN_DIR | EF_NAME_ONLY, L"invalid name", NULL, L"invalid name" },
83     /* name only with dirs in Windows */
84     { 20, 1, BEEF, EF_WIN_DIR | EF_NAME_ONLY, L"", NULL, s_TestDataPath, s_Dirs },
85     { 21, 1, ERROR_FILE_NOT_FOUND, EF_WIN_DIR | EF_NAME_ONLY, L"Fonts", L"Fonts", NULL, s_Dirs },
86     { 22, 0, ERROR_FILE_NOT_FOUND, EF_WIN_DIR | EF_NAME_ONLY, L"notepad", NULL, L"notepad", s_Dirs },
87     { 23, 1, ERROR_FILE_NOT_FOUND, EF_WIN_DIR | EF_NAME_ONLY, L"notepad.exe", NULL, s_NotepadPath, s_Dirs },
88     { 24, 1, ERROR_FILE_NOT_FOUND, EF_WIN_DIR | EF_NAME_ONLY, L"system32", L"system32", NULL, s_Dirs },
89     { 25, 0, ERROR_FILE_NOT_FOUND, EF_WIN_DIR | EF_NAME_ONLY, L"invalid name", NULL, L"invalid name", s_Dirs },
90     /* path without dirs in system32 */
91     { 26, 0, BEEF, EF_SYS_DIR, L"", L"" },
92     { 27, 0, BEEF, EF_SYS_DIR, L"Fonts", L"Fonts" },
93     { 28, 0, BEEF, EF_SYS_DIR, L"notepad", L"notepad" },
94     { 29, 0, BEEF, EF_SYS_DIR, L"notepad.exe", L"notepad.exe" },
95     { 30, 0, BEEF, EF_SYS_DIR, L"system32", L"system32" },
96     { 31, 0, BEEF, EF_SYS_DIR, L"invalid name", L"invalid name" },
97     /* path with dirs in system32 */
98     { 32, 0, BEEF, EF_SYS_DIR, L"", L"", NULL, s_Dirs },
99     { 33, 0, BEEF, EF_SYS_DIR, L"Fonts", L"Fonts", NULL, s_Dirs },
100     { 34, 0, BEEF, EF_SYS_DIR, L"notepad", L"notepad", NULL, s_Dirs },
101     { 35, 0, BEEF, EF_SYS_DIR, L"notepad.exe", L"notepad.exe", NULL, s_Dirs },
102     { 36, 0, BEEF, EF_SYS_DIR, L"system32", L"system32", NULL, s_Dirs },
103     { 37, 0, BEEF, EF_SYS_DIR, L"invalid name", L"invalid name", NULL, s_Dirs },
104     /* name only without dirs in system32 */
105     { 38, 1, BEEF, EF_SYS_DIR | EF_NAME_ONLY, L"", NULL, s_SysDir },
106     { 39, 1, ERROR_FILE_NOT_FOUND, EF_SYS_DIR | EF_NAME_ONLY, L"Fonts", NULL, s_FontsDir },
107     { 40, 0, ERROR_FILE_NOT_FOUND, EF_SYS_DIR | EF_NAME_ONLY, L"notepad", NULL, L"notepad" },
108     { 41, 1, BEEF, EF_SYS_DIR | EF_NAME_ONLY, L"notepad.exe", L"notepad.exe" },
109     { 42, 1, ERROR_FILE_NOT_FOUND, EF_SYS_DIR | EF_NAME_ONLY, L"system32", NULL, s_SysDir },
110     { 43, 0, ERROR_FILE_NOT_FOUND, EF_SYS_DIR | EF_NAME_ONLY, L"invalid name", NULL, L"invalid name" },
111     /* name only with dirs in system32 */
112     { 44, 1, BEEF, EF_SYS_DIR | EF_NAME_ONLY, L"", NULL, s_TestDataPath, s_Dirs },
113     { 45, 1, ERROR_FILE_NOT_FOUND, EF_SYS_DIR | EF_NAME_ONLY, L"Fonts", NULL, s_FontsDir, s_Dirs },
114     { 46, 0, ERROR_FILE_NOT_FOUND, EF_SYS_DIR | EF_NAME_ONLY, L"notepad", NULL, L"notepad", s_Dirs },
115     { 47, 1, ERROR_FILE_NOT_FOUND, EF_SYS_DIR | EF_NAME_ONLY, L"notepad.exe", L"notepad.exe", NULL, s_Dirs },
116     { 48, 1, ERROR_FILE_NOT_FOUND, EF_SYS_DIR | EF_NAME_ONLY, L"system32", NULL, s_SysDir, s_Dirs },
117     { 49, 0, ERROR_FILE_NOT_FOUND, EF_SYS_DIR | EF_NAME_ONLY, L"invalid name", NULL, L"invalid name", s_Dirs },
118     /* path without dirs in testdata dir */
119     { 50, 0, BEEF, EF_TESTDATA, L"", L"" },
120     { 51, 0, BEEF, EF_TESTDATA, L"Fonts", L"Fonts" },
121     { 52, 0, BEEF, EF_TESTDATA, L"notepad", L"notepad" },
122     { 53, 0, BEEF, EF_TESTDATA, L"notepad.exe", L"notepad.exe" },
123     { 54, 0, BEEF, EF_TESTDATA, L"system32", L"system32" },
124     { 55, 0, BEEF, EF_TESTDATA, L"invalid name", L"invalid name" },
125     { 56, 0, BEEF, EF_TESTDATA, L"README.txt", L"README.txt" },
126     { 57, 0, BEEF, EF_TESTDATA, L"CmdLineUtils", L"CmdLineUtils" },
127     { 58, 0, BEEF, EF_TESTDATA, L"CmdLineUtils.exe", L"CmdLineUtils.exe" },
128     /* path with dirs in testdata dir */
129     { 59, 0, BEEF, EF_TESTDATA, L"", L"", NULL, s_Dirs },
130     { 60, 0, BEEF, EF_TESTDATA, L"Fonts", L"Fonts", NULL, s_Dirs },
131     { 61, 0, BEEF, EF_TESTDATA, L"notepad", L"notepad", NULL, s_Dirs },
132     { 62, 0, BEEF, EF_TESTDATA, L"notepad.exe", L"notepad.exe", NULL, s_Dirs },
133     { 63, 0, BEEF, EF_TESTDATA, L"system32", L"system32", NULL, s_Dirs },
134     { 64, 0, BEEF, EF_TESTDATA, L"invalid name", L"invalid name", NULL, s_Dirs },
135     { 65, 0, BEEF, EF_TESTDATA, L"README.txt", L"README.txt", NULL, s_Dirs },
136     { 66, 0, BEEF, EF_TESTDATA, L"CmdLineUtils", L"CmdLineUtils", NULL, s_Dirs },
137     { 67, 0, BEEF, EF_TESTDATA, L"CmdLineUtils.exe", L"CmdLineUtils.exe", NULL, s_Dirs },
138     /* name only without dirs in testdata dir */
139     { 68, 1, BEEF, EF_TESTDATA | EF_NAME_ONLY, L"", NULL, s_SysDir },
140     { 69, 1, ERROR_FILE_NOT_FOUND, EF_TESTDATA | EF_NAME_ONLY, L"Fonts", NULL, s_FontsDir },
141     { 70, 0, ERROR_FILE_NOT_FOUND, EF_TESTDATA | EF_NAME_ONLY, L"notepad", NULL, L"notepad" },
142     { 71, 1, BEEF, EF_TESTDATA | EF_NAME_ONLY, L"notepad.exe", NULL, s_NotepadPath },
143     { 72, 1, ERROR_FILE_NOT_FOUND, EF_TESTDATA | EF_NAME_ONLY, L"system32", NULL, s_SysDir },
144     { 73, 0, ERROR_FILE_NOT_FOUND, EF_TESTDATA | EF_NAME_ONLY, L"invalid name", NULL, L"invalid name" },
145     { 74, 1, BEEF, EF_TESTDATA | EF_NAME_ONLY, L"README.txt", L"README.txt", NULL, s_Dirs },
146     { 75, 0, ERROR_FILE_NOT_FOUND, EF_TESTDATA | EF_NAME_ONLY, L"CmdLineUtils", NULL, L"CmdLineUtils", s_Dirs },
147     { 76, 0, ERROR_FILE_NOT_FOUND, EF_TESTDATA | EF_NAME_ONLY, L"CmdLineUtils.exe", NULL, L"CmdLineUtils.exe", s_Dirs },
148     /* name only with dirs in testdata dir */
149     { 77, 1, BEEF, EF_TESTDATA | EF_NAME_ONLY, L"", NULL, s_TestDataPath, s_Dirs },
150     { 78, 1, ERROR_FILE_NOT_FOUND, EF_TESTDATA | EF_NAME_ONLY, L"Fonts", NULL, s_FontsDir, s_Dirs },
151     { 79, 0, ERROR_FILE_NOT_FOUND, EF_TESTDATA | EF_NAME_ONLY, L"notepad", NULL, L"notepad", s_Dirs },
152     { 80, 1, ERROR_FILE_NOT_FOUND, EF_TESTDATA | EF_NAME_ONLY, L"notepad.exe", NULL, s_NotepadPath, s_Dirs },
153     { 81, 1, ERROR_FILE_NOT_FOUND, EF_TESTDATA | EF_NAME_ONLY, L"system32", NULL, s_SysDir, s_Dirs },
154     { 82, 0, ERROR_FILE_NOT_FOUND, EF_TESTDATA | EF_NAME_ONLY, L"invalid name", NULL, L"invalid name", s_Dirs },
155     { 83, 1, BEEF, EF_TESTDATA | EF_NAME_ONLY, L"README.txt", L"README.txt", NULL, s_Dirs },
156     { 84, 0, ERROR_FILE_NOT_FOUND, EF_TESTDATA | EF_NAME_ONLY, L"CmdLineUtils", NULL, L"CmdLineUtils", s_Dirs },
157     { 85, 0, ERROR_FILE_NOT_FOUND, EF_TESTDATA | EF_NAME_ONLY, L"CmdLineUtils.exe", NULL, L"CmdLineUtils.exe", s_Dirs },
158 };
159 
DoEntry(INT EntryNumber,const ENTRY * pEntry,const WCHAR * PathVar)160 static void DoEntry(INT EntryNumber, const ENTRY *pEntry, const WCHAR *PathVar)
161 {
162     WCHAR Path[MAX_PATH], PathExpected[MAX_PATH], OldPathVar[256];
163     INT Ret;
164     DWORD Error;
165 
166     if (pEntry->NameBefore == NULL)
167     {
168         assert(pEntry->NameExpected == NULL);
169         assert(pEntry->PathExpected == NULL);
170     }
171 
172     switch (pEntry->EF_ & EF_TYPE_MASK)
173     {
174     case EF_FULLPATH:
175         if (pEntry->NameBefore)
176         {
177             lstrcpyW(Path, pEntry->NameBefore);
178         }
179         if (pEntry->NameExpected)
180         {
181             lstrcpyW(PathExpected, pEntry->NameExpected);
182         }
183         break;
184 
185     case EF_TESTDATA:
186         if (pEntry->EF_ & EF_NAME_ONLY)
187         {
188             lstrcpyW(Path, pEntry->NameBefore);
189         }
190         else
191         {
192             lstrcpyW(Path, s_TestDataPath);
193             lstrcatW(Path, L"\\");
194             lstrcatW(Path, pEntry->NameBefore);
195         }
196 
197         if (pEntry->NameExpected)
198         {
199             lstrcpyW(PathExpected, s_TestDataPath);
200             lstrcatW(PathExpected, L"\\");
201             lstrcatW(PathExpected, pEntry->NameExpected);
202         }
203         break;
204 
205     case EF_WIN_DIR:
206         if (pEntry->EF_ & EF_NAME_ONLY)
207         {
208             lstrcpyW(Path, pEntry->NameBefore);
209         }
210         else
211         {
212             GetWindowsDirectoryW(Path, _countof(Path));
213             lstrcatW(Path, L"\\");
214             lstrcatW(Path, pEntry->NameBefore);
215         }
216 
217         if (pEntry->NameExpected)
218         {
219             GetWindowsDirectoryW(PathExpected, _countof(PathExpected));
220             lstrcatW(PathExpected, L"\\");
221             lstrcatW(PathExpected, pEntry->NameExpected);
222         }
223         break;
224 
225     case EF_SYS_DIR:
226         if (pEntry->EF_ & EF_NAME_ONLY)
227         {
228             lstrcpyW(Path, pEntry->NameBefore);
229         }
230         else
231         {
232             GetSystemDirectoryW(Path, _countof(Path));
233             lstrcatW(Path, L"\\");
234             lstrcatW(Path, pEntry->NameBefore);
235         }
236 
237         if (pEntry->NameExpected)
238         {
239             GetSystemDirectoryW(PathExpected, _countof(PathExpected));
240             lstrcatW(PathExpected, L"\\");
241             lstrcatW(PathExpected, pEntry->NameExpected);
242         }
243         break;
244     }
245 
246     if (PathVar)
247     {
248         if (!GetEnvironmentVariableW(L"PATH", OldPathVar, _countof(OldPathVar)))
249         {
250             skip("#%d: GetEnvironmentVariableW failed\n", EntryNumber);
251             return;
252         }
253         if (!SetEnvironmentVariableW(L"PATH", PathVar))
254         {
255             skip("#%d: SetEnvironmentVariableW failed\n", EntryNumber);
256             return;
257         }
258     }
259 
260     _SEH2_TRY
261     {
262         SetLastError(BEEF);
263         if (pEntry->NameBefore)
264         {
265             Ret = PathFindOnPathW(Path, (LPCWSTR *)pEntry->Dirs);
266         }
267         else
268         {
269             Ret = PathFindOnPathW(NULL, (LPCWSTR *)pEntry->Dirs);
270         }
271         Error = GetLastError();
272     }
273     _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
274     {
275         Ret = RAISED;
276         Error = DEAD;
277     }
278     _SEH2_END;
279 
280     if (PathVar)
281     {
282         ok(SetEnvironmentVariableW(L"PATH", OldPathVar),
283            "#%d: SetEnvironmentVariableW failed\n", EntryNumber);
284     }
285 
286     ok(Ret == pEntry->Ret, "#%d: Ret expected %d, was %d.\n",
287        EntryNumber, pEntry->Ret, Ret);
288 
289     if (pEntry->Error != IGNORE_ERR)
290     {
291         ok(Error == pEntry->Error, "#%d: last error expected %ld, was %ld.\n",
292            EntryNumber, pEntry->Error, Error);
293     }
294 
295     if (pEntry->PathExpected)
296     {
297         ok(lstrcmpiW(Path, pEntry->PathExpected) == 0, "#%d: Path expected %s, was %s.\n",
298            EntryNumber, wine_dbgstr_w(pEntry->PathExpected), wine_dbgstr_w(Path));
299     }
300     else if (pEntry->NameExpected)
301     {
302         ok(lstrcmpiW(Path, PathExpected) == 0, "#%d: Path expected %s, was %s.\n",
303            EntryNumber, wine_dbgstr_w(PathExpected), wine_dbgstr_w(Path));
304     }
305 }
306 
Test_PathFindOnPathW(void)307 static void Test_PathFindOnPathW(void)
308 {
309     UINT i;
310 
311     for (i = 0; i < _countof(s_Entries); ++i)
312     {
313         DoEntry(s_Entries[i].EntryNumber, &s_Entries[i], NULL);
314     }
315 }
316 
START_TEST(PathFindOnPath)317 START_TEST(PathFindOnPath)
318 {
319     LPWSTR pch;
320 
321     GetWindowsDirectoryW(s_WinDir, _countof(s_WinDir));
322     GetSystemDirectoryW(s_SysDir, _countof(s_SysDir));
323 
324     lstrcpyW(s_FontsDir, s_WinDir);
325     lstrcatW(s_FontsDir, L"\\Fonts");
326 
327     lstrcpyW(s_NotepadPath, s_SysDir);
328     lstrcatW(s_NotepadPath, L"\\notepad.exe");
329 
330     GetModuleFileNameW(NULL, s_TestDataPath, _countof(s_TestDataPath));
331     pch = wcsrchr(s_TestDataPath, L'\\');
332     if (pch == NULL)
333         pch = wcsrchr(s_TestDataPath, L'/');
334     if (pch == NULL)
335     {
336         skip("GetModuleFileName and/or wcsrchr are insane.\n");
337         return;
338     }
339     lstrcpyW(pch, L"\\testdata");
340     if (GetFileAttributesW(s_TestDataPath) == INVALID_FILE_ATTRIBUTES)
341     {
342         skip("testdata is not found.\n");
343         return;
344     }
345 
346     s_Dirs[0] = s_TestDataPath;
347     s_Dirs[1] = NULL;
348 
349     Test_PathFindOnPathW();
350 }
351