1 /*
2  * Unit test suite for dir functions
3  *
4  * Copyright 2006 CodeWeavers, Aric Stewart
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2.1 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this library; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
19  */
20 
21 #include "wine/test.h"
22 #include <stdarg.h>
23 #include <stdlib.h>
24 #include <stdio.h>
25 #include <fcntl.h>
26 #include <direct.h>
27 #include <sys/stat.h>
28 #include <io.h>
29 #include <mbctype.h>
30 #include <windef.h>
31 #include <winbase.h>
32 #include <winnls.h>
33 #include <process.h>
34 #include <errno.h>
35 
36 static int (__cdecl *p_makepath_s)(char *, size_t, const char *, const char *, const char *, const char *);
37 static int (__cdecl *p_wmakepath_s)(wchar_t *, size_t, const wchar_t *,const wchar_t *, const wchar_t *, const wchar_t *);
38 static int (__cdecl *p_searchenv_s)(const char*, const char*, char*, size_t);
39 static int (__cdecl *p_wsearchenv_s)(const wchar_t*, const wchar_t*, wchar_t*, size_t);
40 
41 static void init(void)
42 {
43     HMODULE hmod = GetModuleHandleA("msvcrt.dll");
44 
45     p_makepath_s = (void *)GetProcAddress(hmod, "_makepath_s");
46     p_wmakepath_s = (void *)GetProcAddress(hmod, "_wmakepath_s");
47     p_searchenv_s = (void *)GetProcAddress(hmod, "_searchenv_s");
48     p_wsearchenv_s = (void *)GetProcAddress(hmod, "_wsearchenv_s");
49 }
50 
51 typedef struct
52 {
53     const char* buffer;
54     const char* drive;
55     const char* dir;
56     const char* file;
57     const char* ext;
58     const char* expected;
59 } makepath_case;
60 
61 #define USE_BUFF ((char*)~0ul)
62 static const makepath_case makepath_cases[] =
63 {
64     { NULL, NULL, NULL, NULL, NULL, "" }, /* 0 */
65     { NULL, "c", NULL, NULL, NULL, "c:" },
66     { NULL, "c:", NULL, NULL, NULL, "c:" },
67     { NULL, "c:\\", NULL, NULL, NULL, "c:" },
68     { NULL, NULL, "dir", NULL, NULL, "dir\\" },
69     { NULL, NULL, "dir\\", NULL, NULL, "dir\\" },
70     { NULL, NULL, "\\dir", NULL, NULL, "\\dir\\" },
71     { NULL, NULL, NULL, "file", NULL, "file" },
72     { NULL, NULL, NULL, "\\file", NULL, "\\file" },
73     { NULL, NULL, NULL, "file", NULL, "file" },
74     { NULL, NULL, NULL, NULL, "ext", ".ext" }, /* 10 */
75     { NULL, NULL, NULL, NULL, ".ext", ".ext" },
76     { "foo", NULL, NULL, NULL, NULL, "" },
77     { "foo", USE_BUFF, NULL, NULL, NULL, "f:" },
78     { "foo", NULL, USE_BUFF, NULL, NULL, "foo\\" },
79     { "foo", NULL, NULL, USE_BUFF, NULL, "foo" },
80     { "foo", NULL, USE_BUFF, "file", NULL, "foo\\file" },
81     { "foo", NULL, USE_BUFF, "file", "ext", "foo\\file.ext" },
82     { "foo", NULL, NULL, USE_BUFF, "ext", "foo.ext" },
83     /* remaining combinations of USE_BUFF crash native */
84     { NULL, "c", "dir", "file", "ext", "c:dir\\file.ext" },
85     { NULL, "c:", "dir", "file", "ext", "c:dir\\file.ext" }, /* 20 */
86     { NULL, "c:\\", "dir", "file", "ext", "c:dir\\file.ext" }
87 };
88 
89 static void test_makepath(void)
90 {
91     WCHAR driveW[MAX_PATH];
92     WCHAR dirW[MAX_PATH];
93     WCHAR fileW[MAX_PATH];
94     WCHAR extW[MAX_PATH];
95     WCHAR bufferW[MAX_PATH];
96     char buffer[MAX_PATH];
97 
98     unsigned int i, n;
99 
100     for (i = 0; i < ARRAY_SIZE(makepath_cases); ++i)
101     {
102         const makepath_case* p = &makepath_cases[i];
103 
104         memset(buffer, 'X', MAX_PATH);
105         if (p->buffer)
106             strcpy(buffer, p->buffer);
107 
108         /* Ascii */
109         _makepath(buffer,
110                   p->drive == USE_BUFF ? buffer : p->drive,
111                   p->dir == USE_BUFF ? buffer : p->dir,
112                   p->file == USE_BUFF? buffer : p->file,
113                   p->ext == USE_BUFF ? buffer : p->ext);
114 
115         buffer[MAX_PATH - 1] = '\0';
116         ok(!strcmp(p->expected, buffer), "got '%s' for case %d\n", buffer, i);
117 
118         /* Unicode */
119         if (p->drive != USE_BUFF) MultiByteToWideChar(CP_ACP, 0, p->drive, -1, driveW, MAX_PATH);
120         if (p->dir != USE_BUFF) MultiByteToWideChar(CP_ACP, 0, p->dir, -1, dirW, MAX_PATH);
121         if (p->file != USE_BUFF) MultiByteToWideChar(CP_ACP, 0, p->file, -1, fileW, MAX_PATH);
122         if (p->ext != USE_BUFF) MultiByteToWideChar(CP_ACP, 0, p->ext, -1, extW, MAX_PATH);
123 
124         memset(buffer, 0, MAX_PATH);
125         for (n = 0; n < MAX_PATH; ++n)
126             bufferW[n] = 'X';
127         if (p->buffer) MultiByteToWideChar( CP_ACP, 0, p->buffer, -1, bufferW, MAX_PATH);
128 
129         _wmakepath(bufferW,
130                    p->drive == USE_BUFF ? bufferW : p->drive ? driveW : NULL,
131                    p->dir == USE_BUFF ? bufferW : p->dir ? dirW : NULL,
132                    p->file == USE_BUFF? bufferW : p->file ? fileW : NULL,
133                    p->ext == USE_BUFF ? bufferW : p->ext ? extW : NULL);
134 
135         bufferW[MAX_PATH - 1] = '\0';
136         WideCharToMultiByte(CP_ACP, 0, bufferW, -1, buffer, MAX_PATH, NULL, NULL);
137         ok(!strcmp(p->expected, buffer), "got '%s' for unicode case %d\n", buffer, i);
138     }
139 }
140 
141 static const WCHAR expected0[] = {'\0','X','X','X','X','X','X','X','X','X','X','X','X'};
142 static const WCHAR expected1[] = {'\0','X','X','X','X','X','X','X','X','X','X','X','X'};
143 static const WCHAR expected2[] = {'\0',':','X','X','X','X','X','X','X','X','X','X','X'};
144 static const WCHAR expected3[] = {'\0',':','d','X','X','X','X','X','X','X','X','X','X'};
145 static const WCHAR expected4[] = {'\0',':','d','\\','X','X','X','X','X','X','X','X','X'};
146 static const WCHAR expected5[] = {'\0',':','d','\\','f','X','X','X','X','X','X','X','X'};
147 static const WCHAR expected6[] = {'\0',':','d','\\','f','i','X','X','X','X','X','X','X'};
148 static const WCHAR expected7[] = {'\0',':','d','\\','f','i','l','X','X','X','X','X','X'};
149 static const WCHAR expected8[] = {'\0',':','d','\\','f','i','l','e','X','X','X','X','X'};
150 static const WCHAR expected9[] = {'\0',':','d','\\','f','i','l','e','.','X','X','X','X'};
151 static const WCHAR expected10[] = {'\0',':','d','\\','f','i','l','e','.','e','X','X','X'};
152 static const WCHAR expected11[] = {'\0',':','d','\\','f','i','l','e','.','e','x','X','X'};
153 
154 static const WCHAR expected12[] = {'\0',':','X','X','X','X','X','X','X','X'};
155 static const WCHAR expected13[] = {'\0',':','d','X','X','X','X','X','X','X'};
156 static const WCHAR expected14[] = {'\0',':','d','i','X','X','X','X','X','X'};
157 static const WCHAR expected15[] = {'\0',':','d','i','r','X','X','X','X','X'};
158 static const WCHAR expected16[] = {'\0',':','d','i','r','\\','X','X','X','X'};
159 
160 static const WCHAR expected17[] = {'\0','o','o'};
161 static const WCHAR expected18[] = {'\0','o','o','\0','X'};
162 static const WCHAR expected19[] = {'\0','o','o','\0'};
163 static const WCHAR expected20[] = {'\0','o','o','\0','X','X','X','X','X'};
164 static const WCHAR expected21[] = {'\0','o','o','\\','f','i','l','X','X'};
165 static const WCHAR expected22[] = {'\0','o','o','\0','X','X','X','X','X','X','X','X','X'};
166 static const WCHAR expected23[] = {'\0','o','o','\\','f','i','l','X','X','X','X','X','X'};
167 static const WCHAR expected24[] = {'\0','o','o','\\','f','i','l','e','.','e','x','X','X'};
168 static const WCHAR expected25[] = {'\0','o','o','\0','X','X','X','X'};
169 static const WCHAR expected26[] = {'\0','o','o','.','e','x','X','X'};
170 
171 typedef struct
172 {
173     const char* buffer;
174     size_t length;
175     const char* drive;
176     const char* dir;
177     const char* file;
178     const char* ext;
179     const char* expected;
180     const WCHAR *expected_unicode;
181     size_t expected_length;
182 } makepath_s_case;
183 
184 static const makepath_s_case makepath_s_cases[] =
185 {
186     /* Behavior with directory parameter containing backslash. */
187     {NULL, 1, "c:", "d\\", "file", "ext", "\0XXXXXXXXXXXX", expected0, 13},
188     {NULL, 2, "c:", "d\\", "file", "ext", "\0XXXXXXXXXXXX", expected1, 13},
189     {NULL, 3, "c:", "d\\", "file", "ext", "\0:XXXXXXXXXXX", expected2, 13},
190     {NULL, 4, "c:", "d\\", "file", "ext", "\0:dXXXXXXXXXX", expected3, 13},
191     {NULL, 5, "c:", "d\\", "file", "ext", "\0:d\\XXXXXXXXX", expected4, 13},
192     {NULL, 6, "c:", "d\\", "file", "ext", "\0:d\\fXXXXXXXX", expected5, 13},
193     {NULL, 7, "c:", "d\\", "file", "ext", "\0:d\\fiXXXXXXX", expected6, 13},
194     {NULL, 8, "c:", "d\\", "file", "ext", "\0:d\\filXXXXXX", expected7, 13},
195     {NULL, 9, "c:", "d\\", "file", "ext", "\0:d\\fileXXXXX", expected8, 13},
196     {NULL, 10, "c:", "d\\", "file", "ext", "\0:d\\file.XXXX", expected9, 13},
197     {NULL, 11, "c:", "d\\", "file", "ext", "\0:d\\file.eXXX", expected10, 13},
198     {NULL, 12, "c:", "d\\", "file", "ext", "\0:d\\file.exXX", expected11, 13},
199     /* Behavior with directory parameter lacking backslash. */
200     {NULL, 3, "c:", "dir", "f", "ext", "\0:XXXXXXXX", expected12, 10},
201     {NULL, 4, "c:", "dir", "f", "ext", "\0:dXXXXXXX", expected13, 10},
202     {NULL, 5, "c:", "dir", "f", "ext", "\0:diXXXXXX", expected14, 10},
203     {NULL, 6, "c:", "dir", "f", "ext", "\0:dirXXXXX", expected15, 10},
204     {NULL, 7, "c:", "dir", "f", "ext", "\0:dir\\XXXX", expected16, 10},
205     /* Behavior with overlapped buffer. */
206     {"foo", 2, USE_BUFF, NULL, NULL, NULL, "\0oo", expected17, 3},
207     {"foo", 4, NULL, USE_BUFF, NULL, NULL, "\0oo\0X", expected18, 5},
208     {"foo", 3, NULL, NULL, USE_BUFF, NULL, "\0oo\0", expected19, 4},
209     {"foo", 4, NULL, USE_BUFF, "file", NULL, "\0oo\0XXXXX", expected20, 9},
210     {"foo", 8, NULL, USE_BUFF, "file", NULL, "\0oo\\filXX", expected21, 9},
211     {"foo", 4, NULL, USE_BUFF, "file", "ext", "\0oo\0XXXXXXXXX", expected22, 13},
212     {"foo", 8, NULL, USE_BUFF, "file", "ext", "\0oo\\filXXXXXX", expected23, 13},
213     {"foo", 12, NULL, USE_BUFF, "file", "ext", "\0oo\\file.exXX", expected24, 13},
214     {"foo", 4, NULL, NULL, USE_BUFF, "ext", "\0oo\0XXXX", expected25, 8},
215     {"foo", 7, NULL, NULL, USE_BUFF, "ext", "\0oo.exXX", expected26, 8},
216 };
217 
218 static void test_makepath_s(void)
219 {
220     WCHAR driveW[MAX_PATH];
221     WCHAR dirW[MAX_PATH];
222     WCHAR fileW[MAX_PATH];
223     WCHAR extW[MAX_PATH];
224     WCHAR bufferW[MAX_PATH];
225     char buffer[MAX_PATH];
226     int ret;
227     unsigned int i, n;
228 
229     if (!p_makepath_s || !p_wmakepath_s)
230     {
231         win_skip("Safe makepath functions are not available\n");
232         return;
233     }
234 
235     errno = EBADF;
236     ret = p_makepath_s(NULL, 0, NULL, NULL, NULL, NULL);
237     ok(ret == EINVAL, "Expected _makepath_s to return EINVAL, got %d\n", ret);
238     ok(errno == EINVAL, "Expected errno to be EINVAL, got %d\n", errno);
239 
240     errno = EBADF;
241     ret = p_makepath_s(buffer, 0, NULL, NULL, NULL, NULL);
242     ok(ret == EINVAL, "Expected _makepath_s to return EINVAL, got %d\n", ret);
243     ok(errno == EINVAL, "Expected errno to be EINVAL, got %d\n", errno);
244 
245     errno = EBADF;
246     ret = p_wmakepath_s(NULL, 0, NULL, NULL, NULL, NULL);
247     ok(ret == EINVAL, "Expected _wmakepath_s to return EINVAL, got %d\n", ret);
248     ok(errno == EINVAL, "Expected errno to be EINVAL, got %d\n", errno);
249 
250     errno = EBADF;
251     ret = p_wmakepath_s(bufferW, 0, NULL, NULL, NULL, NULL);
252     ok(ret == EINVAL, "Expected _wmakepath_s to return EINVAL, got %d\n", ret);
253     ok(errno == EINVAL, "Expected errno to be EINVAL, got %d\n", errno);
254 
255     /* Test with the normal _makepath cases. */
256     for (i = 0; i < ARRAY_SIZE(makepath_cases); i++)
257     {
258         const makepath_case *p = makepath_cases + i;
259 
260         memset(buffer, 'X', MAX_PATH);
261         if (p->buffer)
262             strcpy(buffer, p->buffer);
263 
264         /* Ascii */
265         ret = p_makepath_s(buffer, MAX_PATH,
266                            p->drive == USE_BUFF ? buffer : p->drive,
267                            p->dir == USE_BUFF ? buffer : p->dir,
268                            p->file == USE_BUFF? buffer : p->file,
269                            p->ext == USE_BUFF ? buffer : p->ext);
270         ok(ret == 0, "[%d] Expected _makepath_s to return 0, got %d\n", i, ret);
271 
272         buffer[MAX_PATH - 1] = '\0';
273         ok(!strcmp(p->expected, buffer), "got '%s' for case %d\n", buffer, i);
274 
275         /* Unicode */
276         if (p->drive != USE_BUFF) MultiByteToWideChar(CP_ACP, 0, p->drive, -1, driveW, MAX_PATH);
277         if (p->dir != USE_BUFF) MultiByteToWideChar(CP_ACP, 0, p->dir, -1, dirW, MAX_PATH);
278         if (p->file != USE_BUFF) MultiByteToWideChar(CP_ACP, 0, p->file, -1, fileW, MAX_PATH);
279         if (p->ext != USE_BUFF) MultiByteToWideChar(CP_ACP, 0, p->ext, -1, extW, MAX_PATH);
280 
281         memset(buffer, 0, MAX_PATH);
282         for (n = 0; n < MAX_PATH; ++n)
283             bufferW[n] = 'X';
284         if (p->buffer) MultiByteToWideChar( CP_ACP, 0, p->buffer, -1, bufferW, MAX_PATH);
285 
286         ret = p_wmakepath_s(bufferW, MAX_PATH,
287                             p->drive == USE_BUFF ? bufferW : p->drive ? driveW : NULL,
288                             p->dir == USE_BUFF ? bufferW : p->dir ? dirW : NULL,
289                             p->file == USE_BUFF? bufferW : p->file ? fileW : NULL,
290                             p->ext == USE_BUFF ? bufferW : p->ext ? extW : NULL);
291         ok(ret == 0, "[%d] Expected _wmakepath_s to return 0, got %d\n", i, ret);
292 
293         bufferW[MAX_PATH - 1] = '\0';
294         WideCharToMultiByte(CP_ACP, 0, bufferW, -1, buffer, MAX_PATH, NULL, NULL);
295         ok(!strcmp(p->expected, buffer), "got '%s' for unicode case %d\n", buffer, i);
296     }
297 
298     /* Try insufficient length cases. */
299     for (i = 0; i < ARRAY_SIZE(makepath_s_cases); i++)
300     {
301         const makepath_s_case *p = makepath_s_cases + i;
302 
303         memset(buffer, 'X', MAX_PATH);
304         if (p->buffer)
305             strcpy(buffer, p->buffer);
306 
307         /* Ascii */
308         errno = EBADF;
309         ret = p_makepath_s(buffer, p->length,
310                            p->drive == USE_BUFF ? buffer : p->drive,
311                            p->dir == USE_BUFF ? buffer : p->dir,
312                            p->file == USE_BUFF? buffer : p->file,
313                            p->ext == USE_BUFF ? buffer : p->ext);
314         ok(ret == ERANGE, "[%d] Expected _makepath_s to return ERANGE, got %d\n", i, ret);
315         ok(errno == ERANGE, "[%d] Expected errno to be ERANGE, got %d\n", i, errno);
316         ok(!memcmp(p->expected, buffer, p->expected_length), "unexpected output for case %d\n", i);
317 
318         /* Unicode */
319         if (p->drive != USE_BUFF) MultiByteToWideChar(CP_ACP, 0, p->drive, -1, driveW, MAX_PATH);
320         if (p->dir != USE_BUFF) MultiByteToWideChar(CP_ACP, 0, p->dir, -1, dirW, MAX_PATH);
321         if (p->file != USE_BUFF) MultiByteToWideChar(CP_ACP, 0, p->file, -1, fileW, MAX_PATH);
322         if (p->ext != USE_BUFF) MultiByteToWideChar(CP_ACP, 0, p->ext, -1, extW, MAX_PATH);
323 
324         memset(buffer, 0, MAX_PATH);
325         for (n = 0; n < MAX_PATH; ++n)
326             bufferW[n] = 'X';
327         if (p->buffer) MultiByteToWideChar( CP_ACP, 0, p->buffer, -1, bufferW, MAX_PATH);
328 
329         errno = EBADF;
330         ret = p_wmakepath_s(bufferW, p->length,
331                             p->drive == USE_BUFF ? bufferW : p->drive ? driveW : NULL,
332                             p->dir == USE_BUFF ? bufferW : p->dir ? dirW : NULL,
333                             p->file == USE_BUFF? bufferW : p->file ? fileW : NULL,
334                             p->ext == USE_BUFF ? bufferW : p->ext ? extW : NULL);
335         ok(ret == ERANGE, "[%d] Expected _wmakepath_s to return ERANGE, got %d\n", i, ret);
336         ok(errno == ERANGE, "[%d] Expected errno to be ERANGE, got %d\n", i, errno);
337 
338         ok(!memcmp(p->expected_unicode, bufferW, p->expected_length * sizeof(WCHAR)), "unexpected output for case %d\n", i);
339     }
340 }
341 
342 static void test_fullpath(void)
343 {
344     char full[MAX_PATH];
345     char tmppath[MAX_PATH];
346     char prevpath[MAX_PATH];
347     char level1[MAX_PATH];
348     char level2[MAX_PATH];
349     char teststring[MAX_PATH];
350     char *freeme;
351     BOOL rc,free1,free2;
352 
353     free1=free2=TRUE;
354     GetCurrentDirectoryA(MAX_PATH, prevpath);
355     GetTempPathA(MAX_PATH,tmppath);
356     strcpy(level1,tmppath);
357     strcat(level1,"msvcrt-test\\");
358 
359     rc = CreateDirectoryA(level1,NULL);
360     if (!rc && GetLastError()==ERROR_ALREADY_EXISTS)
361         free1=FALSE;
362 
363     strcpy(level2,level1);
364     strcat(level2,"nextlevel\\");
365     rc = CreateDirectoryA(level2,NULL);
366     if (!rc && GetLastError()==ERROR_ALREADY_EXISTS)
367         free2=FALSE;
368     SetCurrentDirectoryA(level2);
369 
370     ok(_fullpath(full,"test", MAX_PATH)!=NULL,"_fullpath failed\n");
371     strcpy(teststring,level2);
372     strcat(teststring,"test");
373     ok(strcmp(full,teststring)==0,"Invalid Path returned %s\n",full);
374     ok(_fullpath(full,"\\test", MAX_PATH)!=NULL,"_fullpath failed\n");
375     memcpy(teststring,level2,3);
376     teststring[3]=0;
377     strcat(teststring,"test");
378     ok(strcmp(full,teststring)==0,"Invalid Path returned %s\n",full);
379     ok(_fullpath(full,"..\\test", MAX_PATH)!=NULL,"_fullpath failed\n");
380     strcpy(teststring,level1);
381     strcat(teststring,"test");
382     ok(strcmp(full,teststring)==0,"Invalid Path returned %s\n",full);
383     ok(_fullpath(full,"..\\test", 10)==NULL,"_fullpath failed to generate error\n");
384 
385     freeme = _fullpath(NULL,"test", 0);
386     ok(freeme!=NULL,"No path returned\n");
387     strcpy(teststring,level2);
388     strcat(teststring,"test");
389     ok(strcmp(freeme,teststring)==0,"Invalid Path returned %s\n",freeme);
390     free(freeme);
391 
392     SetCurrentDirectoryA(prevpath);
393     if (free2)
394         RemoveDirectoryA(level2);
395     if (free1)
396         RemoveDirectoryA(level1);
397 }
398 
399 static void test_splitpath(void)
400 {
401     const char* path = "c:\\\x83\x5c\x83\x74\x83\x67.bin";
402     char drive[3], dir[MAX_PATH], fname[MAX_PATH], ext[MAX_PATH];
403     int prev_cp = _getmbcp();
404 
405     /* SBCS codepage */
406     _setmbcp(1252);
407     _splitpath(path, drive, dir, fname, ext);
408     ok(!strcmp(drive, "c:"), "got %s\n", drive);
409     ok(!strcmp(dir, "\\\x83\x5c"), "got %s\n", dir);
410     ok(!strcmp(fname, "\x83\x74\x83\x67"), "got %s\n", fname);
411     ok(!strcmp(ext, ".bin"), "got %s\n", ext);
412 
413     /* MBCS (Japanese) codepage */
414     _setmbcp(932);
415     _splitpath(path, drive, dir, fname, ext);
416     ok(!strcmp(drive, "c:"), "got %s\n", drive);
417     ok(!strcmp(dir, "\\"), "got %s\n", dir);
418     ok(!strcmp(fname, "\x83\x5c\x83\x74\x83\x67"), "got %s\n", fname);
419     ok(!strcmp(ext, ".bin"), "got %s\n", ext);
420 
421     _setmbcp(prev_cp);
422 }
423 
424 static void test_searchenv(void)
425 {
426     const char *dirs[] = {
427         "\\search_env_test",
428         "\\search_env_test\\dir1",
429         "\\search_env_test\\dir2",
430         "\\search_env_test\\dir3longer"
431     };
432 
433     const char *files[] = {
434         "\\search_env_test\\dir1\\1.dat",
435         "\\search_env_test\\dir1\\2.dat",
436         "\\search_env_test\\dir2\\1.dat",
437         "\\search_env_test\\dir2\\3.dat",
438         "\\search_env_test\\dir3longer\\3.dat"
439     };
440 
441     const WCHAR env_w[] = {'T','E','S','T','_','P','A','T','H',0};
442     const WCHAR dat1_w[] = {'1','.','d','a','t',0};
443     const WCHAR dat3_w[] = {'3','.','d','a','t',0};
444 
445     char env1[4*MAX_PATH], env2[4*MAX_PATH], tmppath[MAX_PATH], path[2*MAX_PATH];
446     char result[MAX_PATH], exp[2*MAX_PATH];
447     WCHAR result_w[MAX_PATH];
448     int i, path_len;
449     FILE *tmp_file;
450 
451     if (getenv("TEST_PATH")) {
452         skip("TEST_PATH environment variable already set\n");
453         return;
454     }
455 
456     path_len = GetTempPathA(MAX_PATH, tmppath);
457     ok(path_len, "GetTempPath failed\n");
458     memcpy(path, tmppath, path_len);
459 
460     for (i=0; i<ARRAY_SIZE(dirs); i++) {
461         strcpy(path+path_len, dirs[i]);
462 	ok(!mkdir(path), "mkdir failed (dir = %s)\n", path);
463     }
464 
465     for (i=0; i<ARRAY_SIZE(files); i++) {
466         strcpy(path+path_len, files[i]);
467         tmp_file = fopen(path, "wb");
468 	ok(tmp_file != NULL, "fopen failed (file = %s)\n", path);
469         fclose(tmp_file);
470     }
471 
472     strcpy(env1, "TEST_PATH=");
473     strcpy(env2, "TEST_PATH=;");
474     for (i=1; i<ARRAY_SIZE(dirs); i++) {
475         strcat(env1, tmppath);
476         strcat(env1, dirs[i]);
477         strcat(env1, ";");
478 
479         strcat(env2, tmppath);
480         strcat(env2, dirs[i]);
481         strcat(env2, ";;");
482     }
483 
484     if (!p_searchenv_s || !p_wsearchenv_s)
485         win_skip("searchenv_s or wsearchenv_s function is not available\n");
486 
487     putenv(env1);
488     memset(result, 'x', sizeof(result));
489     _searchenv("fail", "TEST_PATH", result);
490     ok(!result[0], "got %s, expected ''\n", result);
491 
492     if (p_searchenv_s) {
493         memset(result, 'x', sizeof(result));
494         i = p_searchenv_s("fail", "TEST_PATH", result, MAX_PATH);
495         ok(i == ENOENT, "searchenv_s returned %d\n", i);
496         ok(!result[0], "got %s, expected ''\n", result);
497     }
498 
499     memset(result, 'x', sizeof(result));
500     strcpy(exp, tmppath);
501     strcat(exp, files[0]);
502     _searchenv("1.dat", "TEST_PATH", result);
503     ok(!strcmp(result, exp), "got %s, expected '%s'\n", result, exp);
504 
505     if (p_searchenv_s) {
506         memset(result, 'x', sizeof(result));
507         i = p_searchenv_s("1.dat", "TEST_PATH", result, MAX_PATH);
508         ok(!i, "searchenv_s returned %d\n", i);
509         ok(!strcmp(result, exp), "got %s, expected '%s'\n", result, exp);
510     }
511 
512     memset(result_w, 'x', sizeof(result_w));
513     _wsearchenv(dat1_w, env_w, result_w);
514     WideCharToMultiByte(CP_ACP, 0, result_w, -1, result, MAX_PATH, NULL, NULL);
515     ok(!strcmp(result, exp), "got %s, expected '%s'\n", result, exp);
516 
517     if (p_wsearchenv_s) {
518         memset(result_w, 'x', sizeof(result_w));
519         i = p_wsearchenv_s(dat1_w, env_w, result_w, MAX_PATH);
520         ok(!i, "wsearchenv_s returned %d\n", i);
521         ok(!strcmp(result, exp), "got %s, expected '%s'\n", result, exp);
522     }
523 
524     memset(result, 'x', sizeof(result));
525     strcpy(exp, tmppath);
526     strcat(exp, files[3]);
527     _searchenv("3.dat", "TEST_PATH", result);
528     ok(!strcmp(result, exp), "got %s, expected '%s'\n", result, exp);
529 
530     if (p_searchenv_s) {
531         memset(result, 'x', sizeof(result));
532         i = p_searchenv_s("3.dat", "TEST_PATH", result, MAX_PATH);
533         ok(!i, "searchenv_s returned %d\n", i);
534         ok(!strcmp(result, exp), "got %s, expected '%s'\n", result, exp);
535     }
536 
537     memset(result_w, 'x', sizeof(result_w));
538     _wsearchenv(dat3_w, env_w, result_w);
539     WideCharToMultiByte(CP_ACP, 0, result_w, -1, result, MAX_PATH, NULL, NULL);
540     ok(!strcmp(result, exp), "got %s, expected '%s'\n", result, exp);
541 
542     if (p_wsearchenv_s) {
543         memset(result_w, 'x', sizeof(result_w));
544         i = p_wsearchenv_s(dat3_w, env_w, result_w, MAX_PATH);
545         ok(!i, "wsearchenv_s returned %d\n", i);
546         ok(!strcmp(result, exp), "got %s, expected '%s'\n", result, exp);
547     }
548 
549     putenv(env2);
550     memset(result, 'x', sizeof(result));
551     strcpy(exp, tmppath);
552     strcat(exp, files[0]);
553     _searchenv("1.dat", "TEST_PATH", result);
554     ok(!strcmp(result, exp), "got %s, expected '%s'\n", result, exp);
555 
556     if (p_searchenv_s) {
557         memset(result, 'x', sizeof(result));
558         i = p_searchenv_s("1.dat", "TEST_PATH", result, MAX_PATH);
559         ok(!i, "searchenv_s returned %d\n", i);
560         ok(!strcmp(result, exp), "got %s, expected '%s'\n", result, exp);
561     }
562 
563     memset(result_w, 'x', sizeof(result_w));
564     _wsearchenv(dat1_w, env_w, result_w);
565     WideCharToMultiByte(CP_ACP, 0, result_w, -1, result, MAX_PATH, NULL, NULL);
566     ok(!strcmp(result, exp), "got %s, expected '%s'\n", result, exp);
567 
568     if (p_wsearchenv_s) {
569         memset(result_w, 'x', sizeof(result_w));
570         i = p_wsearchenv_s(dat1_w, env_w, result_w, MAX_PATH);
571         ok(!i, "wsearchenv_s returned %d\n", i);
572         ok(!strcmp(result, exp), "got %s, expected '%s'\n", result, exp);
573     }
574 
575     memset(result, 'x', sizeof(result));
576     strcpy(exp, tmppath);
577     strcat(exp, files[3]);
578     _searchenv("3.dat", "TEST_PATH", result);
579     ok(!strcmp(result, exp), "got %s, expected '%s'\n", result, exp);
580 
581     if (p_searchenv_s) {
582         memset(result, 'x', sizeof(result));
583         i = p_searchenv_s("3.dat", "TEST_PATH", result, MAX_PATH);
584         ok(!i, "searchenv_s returned %d\n", i);
585         ok(!strcmp(result, exp), "got %s, expected '%s'\n", result, exp);
586     }
587 
588     memset(result_w, 'x', sizeof(result_w));
589     _wsearchenv(dat3_w, env_w, result_w);
590     WideCharToMultiByte(CP_ACP, 0, result_w, -1, result, MAX_PATH, NULL, NULL);
591     ok(!strcmp(result, exp), "got %s, expected '%s'\n", result, exp);
592 
593     if (p_wsearchenv_s) {
594         memset(result_w, 'x', sizeof(result_w));
595         i = p_wsearchenv_s(dat3_w, env_w, result_w, MAX_PATH);
596         ok(!i, "wsearchenv_s returned %d\n", i);
597         ok(!strcmp(result, exp), "got %s, expected '%s'\n", result, exp);
598     }
599 
600     putenv("TEST_PATH=");
601 
602     for (i=ARRAY_SIZE(files)-1; i>=0; i--) {
603         strcpy(path+path_len, files[i]);
604         ok(!remove(path), "remove failed (file = %s)\n", path);
605     }
606 
607     for (i=ARRAY_SIZE(dirs)-1; i>=0; i--) {
608         strcpy(path+path_len, dirs[i]);
609         ok(!rmdir(path), "rmdir failed (dir = %s)\n", path);
610     }
611 }
612 
613 START_TEST(dir)
614 {
615     init();
616 
617     test_fullpath();
618     test_makepath();
619     test_makepath_s();
620     test_splitpath();
621     test_searchenv();
622 }
623