[MSVCRT_WINETEST]
[reactos.git] / rostests / winetests / msvcrt / dir.c
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 <sys/stat.h>
27 #include <io.h>
28 #include <mbctype.h>
29 #include <windef.h>
30 #include <winbase.h>
31 #include <winnls.h>
32 #include <process.h>
33 #include <errno.h>
34
35 static int (__cdecl *p_makepath_s)(char *, size_t, const char *, const char *, const char *, const char *);
36 static int (__cdecl *p_wmakepath_s)(wchar_t *, size_t, const wchar_t *,const wchar_t *, const wchar_t *, const wchar_t *);
37
38 static void init(void)
39 {
40 HMODULE hmod = GetModuleHandleA("msvcrt.dll");
41
42 p_makepath_s = (void *)GetProcAddress(hmod, "_makepath_s");
43 p_wmakepath_s = (void *)GetProcAddress(hmod, "_wmakepath_s");
44 }
45
46 typedef struct
47 {
48 const char* buffer;
49 const char* drive;
50 const char* dir;
51 const char* file;
52 const char* ext;
53 const char* expected;
54 } makepath_case;
55
56 #define USE_BUFF ((char*)~0ul)
57 static const makepath_case makepath_cases[] =
58 {
59 { NULL, NULL, NULL, NULL, NULL, "" }, /* 0 */
60 { NULL, "c", NULL, NULL, NULL, "c:" },
61 { NULL, "c:", NULL, NULL, NULL, "c:" },
62 { NULL, "c:\\", NULL, NULL, NULL, "c:" },
63 { NULL, NULL, "dir", NULL, NULL, "dir\\" },
64 { NULL, NULL, "dir\\", NULL, NULL, "dir\\" },
65 { NULL, NULL, "\\dir", NULL, NULL, "\\dir\\" },
66 { NULL, NULL, NULL, "file", NULL, "file" },
67 { NULL, NULL, NULL, "\\file", NULL, "\\file" },
68 { NULL, NULL, NULL, "file", NULL, "file" },
69 { NULL, NULL, NULL, NULL, "ext", ".ext" }, /* 10 */
70 { NULL, NULL, NULL, NULL, ".ext", ".ext" },
71 { "foo", NULL, NULL, NULL, NULL, "" },
72 { "foo", USE_BUFF, NULL, NULL, NULL, "f:" },
73 { "foo", NULL, USE_BUFF, NULL, NULL, "foo\\" },
74 { "foo", NULL, NULL, USE_BUFF, NULL, "foo" },
75 { "foo", NULL, USE_BUFF, "file", NULL, "foo\\file" },
76 { "foo", NULL, USE_BUFF, "file", "ext", "foo\\file.ext" },
77 { "foo", NULL, NULL, USE_BUFF, "ext", "foo.ext" },
78 /* remaining combinations of USE_BUFF crash native */
79 { NULL, "c", "dir", "file", "ext", "c:dir\\file.ext" },
80 { NULL, "c:", "dir", "file", "ext", "c:dir\\file.ext" }, /* 20 */
81 { NULL, "c:\\", "dir", "file", "ext", "c:dir\\file.ext" }
82 };
83
84 static void test_makepath(void)
85 {
86 WCHAR driveW[MAX_PATH];
87 WCHAR dirW[MAX_PATH];
88 WCHAR fileW[MAX_PATH];
89 WCHAR extW[MAX_PATH];
90 WCHAR bufferW[MAX_PATH];
91 char buffer[MAX_PATH];
92
93 unsigned int i, n;
94
95 for (i = 0; i < sizeof(makepath_cases)/sizeof(makepath_cases[0]); ++i)
96 {
97 const makepath_case* p = &makepath_cases[i];
98
99 memset(buffer, 'X', MAX_PATH);
100 if (p->buffer)
101 strcpy(buffer, p->buffer);
102
103 /* Ascii */
104 _makepath(buffer,
105 p->drive == USE_BUFF ? buffer : p->drive,
106 p->dir == USE_BUFF ? buffer : p->dir,
107 p->file == USE_BUFF? buffer : p->file,
108 p->ext == USE_BUFF ? buffer : p->ext);
109
110 buffer[MAX_PATH - 1] = '\0';
111 ok(!strcmp(p->expected, buffer), "got '%s' for case %d\n", buffer, i);
112
113 /* Unicode */
114 if (p->drive != USE_BUFF) MultiByteToWideChar(CP_ACP, 0, p->drive, -1, driveW, MAX_PATH);
115 if (p->dir != USE_BUFF) MultiByteToWideChar(CP_ACP, 0, p->dir, -1, dirW, MAX_PATH);
116 if (p->file != USE_BUFF) MultiByteToWideChar(CP_ACP, 0, p->file, -1, fileW, MAX_PATH);
117 if (p->ext != USE_BUFF) MultiByteToWideChar(CP_ACP, 0, p->ext, -1, extW, MAX_PATH);
118
119 memset(buffer, 0, MAX_PATH);
120 for (n = 0; n < MAX_PATH; ++n)
121 bufferW[n] = 'X';
122 if (p->buffer) MultiByteToWideChar( CP_ACP, 0, p->buffer, -1, bufferW, MAX_PATH);
123
124 _wmakepath(bufferW,
125 p->drive == USE_BUFF ? bufferW : p->drive ? driveW : NULL,
126 p->dir == USE_BUFF ? bufferW : p->dir ? dirW : NULL,
127 p->file == USE_BUFF? bufferW : p->file ? fileW : NULL,
128 p->ext == USE_BUFF ? bufferW : p->ext ? extW : NULL);
129
130 bufferW[MAX_PATH - 1] = '\0';
131 WideCharToMultiByte(CP_ACP, 0, bufferW, -1, buffer, MAX_PATH, NULL, NULL);
132 ok(!strcmp(p->expected, buffer), "got '%s' for unicode case %d\n", buffer, i);
133 }
134 }
135
136 static const WCHAR expected0[] = {'\0','X','X','X','X','X','X','X','X','X','X','X','X'};
137 static const WCHAR expected1[] = {'\0','X','X','X','X','X','X','X','X','X','X','X','X'};
138 static const WCHAR expected2[] = {'\0',':','X','X','X','X','X','X','X','X','X','X','X'};
139 static const WCHAR expected3[] = {'\0',':','d','X','X','X','X','X','X','X','X','X','X'};
140 static const WCHAR expected4[] = {'\0',':','d','\\','X','X','X','X','X','X','X','X','X'};
141 static const WCHAR expected5[] = {'\0',':','d','\\','f','X','X','X','X','X','X','X','X'};
142 static const WCHAR expected6[] = {'\0',':','d','\\','f','i','X','X','X','X','X','X','X'};
143 static const WCHAR expected7[] = {'\0',':','d','\\','f','i','l','X','X','X','X','X','X'};
144 static const WCHAR expected8[] = {'\0',':','d','\\','f','i','l','e','X','X','X','X','X'};
145 static const WCHAR expected9[] = {'\0',':','d','\\','f','i','l','e','.','X','X','X','X'};
146 static const WCHAR expected10[] = {'\0',':','d','\\','f','i','l','e','.','e','X','X','X'};
147 static const WCHAR expected11[] = {'\0',':','d','\\','f','i','l','e','.','e','x','X','X'};
148
149 static const WCHAR expected12[] = {'\0',':','X','X','X','X','X','X','X','X'};
150 static const WCHAR expected13[] = {'\0',':','d','X','X','X','X','X','X','X'};
151 static const WCHAR expected14[] = {'\0',':','d','i','X','X','X','X','X','X'};
152 static const WCHAR expected15[] = {'\0',':','d','i','r','X','X','X','X','X'};
153 static const WCHAR expected16[] = {'\0',':','d','i','r','\\','X','X','X','X'};
154
155 static const WCHAR expected17[] = {'\0','o','o'};
156 static const WCHAR expected18[] = {'\0','o','o','\0','X'};
157 static const WCHAR expected19[] = {'\0','o','o','\0'};
158 static const WCHAR expected20[] = {'\0','o','o','\0','X','X','X','X','X'};
159 static const WCHAR expected21[] = {'\0','o','o','\\','f','i','l','X','X'};
160 static const WCHAR expected22[] = {'\0','o','o','\0','X','X','X','X','X','X','X','X','X'};
161 static const WCHAR expected23[] = {'\0','o','o','\\','f','i','l','X','X','X','X','X','X'};
162 static const WCHAR expected24[] = {'\0','o','o','\\','f','i','l','e','.','e','x','X','X'};
163 static const WCHAR expected25[] = {'\0','o','o','\0','X','X','X','X'};
164 static const WCHAR expected26[] = {'\0','o','o','.','e','x','X','X'};
165
166 typedef struct
167 {
168 const char* buffer;
169 size_t length;
170 const char* drive;
171 const char* dir;
172 const char* file;
173 const char* ext;
174 const char* expected;
175 const WCHAR *expected_unicode;
176 size_t expected_length;
177 } makepath_s_case;
178
179 static const makepath_s_case makepath_s_cases[] =
180 {
181 /* Behavior with directory parameter containing backslash. */
182 {NULL, 1, "c:", "d\\", "file", "ext", "\0XXXXXXXXXXXX", expected0, 13},
183 {NULL, 2, "c:", "d\\", "file", "ext", "\0XXXXXXXXXXXX", expected1, 13},
184 {NULL, 3, "c:", "d\\", "file", "ext", "\0:XXXXXXXXXXX", expected2, 13},
185 {NULL, 4, "c:", "d\\", "file", "ext", "\0:dXXXXXXXXXX", expected3, 13},
186 {NULL, 5, "c:", "d\\", "file", "ext", "\0:d\\XXXXXXXXX", expected4, 13},
187 {NULL, 6, "c:", "d\\", "file", "ext", "\0:d\\fXXXXXXXX", expected5, 13},
188 {NULL, 7, "c:", "d\\", "file", "ext", "\0:d\\fiXXXXXXX", expected6, 13},
189 {NULL, 8, "c:", "d\\", "file", "ext", "\0:d\\filXXXXXX", expected7, 13},
190 {NULL, 9, "c:", "d\\", "file", "ext", "\0:d\\fileXXXXX", expected8, 13},
191 {NULL, 10, "c:", "d\\", "file", "ext", "\0:d\\file.XXXX", expected9, 13},
192 {NULL, 11, "c:", "d\\", "file", "ext", "\0:d\\file.eXXX", expected10, 13},
193 {NULL, 12, "c:", "d\\", "file", "ext", "\0:d\\file.exXX", expected11, 13},
194 /* Behavior with directory parameter lacking backslash. */
195 {NULL, 3, "c:", "dir", "f", "ext", "\0:XXXXXXXX", expected12, 10},
196 {NULL, 4, "c:", "dir", "f", "ext", "\0:dXXXXXXX", expected13, 10},
197 {NULL, 5, "c:", "dir", "f", "ext", "\0:diXXXXXX", expected14, 10},
198 {NULL, 6, "c:", "dir", "f", "ext", "\0:dirXXXXX", expected15, 10},
199 {NULL, 7, "c:", "dir", "f", "ext", "\0:dir\\XXXX", expected16, 10},
200 /* Behavior with overlapped buffer. */
201 {"foo", 2, USE_BUFF, NULL, NULL, NULL, "\0oo", expected17, 3},
202 {"foo", 4, NULL, USE_BUFF, NULL, NULL, "\0oo\0X", expected18, 5},
203 {"foo", 3, NULL, NULL, USE_BUFF, NULL, "\0oo\0", expected19, 4},
204 {"foo", 4, NULL, USE_BUFF, "file", NULL, "\0oo\0XXXXX", expected20, 9},
205 {"foo", 8, NULL, USE_BUFF, "file", NULL, "\0oo\\filXX", expected21, 9},
206 {"foo", 4, NULL, USE_BUFF, "file", "ext", "\0oo\0XXXXXXXXX", expected22, 13},
207 {"foo", 8, NULL, USE_BUFF, "file", "ext", "\0oo\\filXXXXXX", expected23, 13},
208 {"foo", 12, NULL, USE_BUFF, "file", "ext", "\0oo\\file.exXX", expected24, 13},
209 {"foo", 4, NULL, NULL, USE_BUFF, "ext", "\0oo\0XXXX", expected25, 8},
210 {"foo", 7, NULL, NULL, USE_BUFF, "ext", "\0oo.exXX", expected26, 8},
211 };
212
213 static void test_makepath_s(void)
214 {
215 WCHAR driveW[MAX_PATH];
216 WCHAR dirW[MAX_PATH];
217 WCHAR fileW[MAX_PATH];
218 WCHAR extW[MAX_PATH];
219 WCHAR bufferW[MAX_PATH];
220 char buffer[MAX_PATH];
221 int ret;
222 unsigned int i, n;
223
224 if (!p_makepath_s || !p_wmakepath_s)
225 {
226 win_skip("Safe makepath functions are not available\n");
227 return;
228 }
229
230 errno = EBADF;
231 ret = p_makepath_s(NULL, 0, NULL, NULL, NULL, NULL);
232 ok(ret == EINVAL, "Expected _makepath_s to return EINVAL, got %d\n", ret);
233 ok(errno == EINVAL, "Expected errno to be EINVAL, got %d\n", errno);
234
235 errno = EBADF;
236 ret = p_makepath_s(buffer, 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_wmakepath_s(NULL, 0, NULL, NULL, NULL, NULL);
242 ok(ret == EINVAL, "Expected _wmakepath_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(bufferW, 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 /* Test with the normal _makepath cases. */
251 for (i = 0; i < sizeof(makepath_cases)/sizeof(makepath_cases[0]); i++)
252 {
253 const makepath_case *p = makepath_cases + i;
254
255 memset(buffer, 'X', MAX_PATH);
256 if (p->buffer)
257 strcpy(buffer, p->buffer);
258
259 /* Ascii */
260 ret = p_makepath_s(buffer, MAX_PATH,
261 p->drive == USE_BUFF ? buffer : p->drive,
262 p->dir == USE_BUFF ? buffer : p->dir,
263 p->file == USE_BUFF? buffer : p->file,
264 p->ext == USE_BUFF ? buffer : p->ext);
265 ok(ret == 0, "[%d] Expected _makepath_s to return 0, got %d\n", i, ret);
266
267 buffer[MAX_PATH - 1] = '\0';
268 ok(!strcmp(p->expected, buffer), "got '%s' for case %d\n", buffer, i);
269
270 /* Unicode */
271 if (p->drive != USE_BUFF) MultiByteToWideChar(CP_ACP, 0, p->drive, -1, driveW, MAX_PATH);
272 if (p->dir != USE_BUFF) MultiByteToWideChar(CP_ACP, 0, p->dir, -1, dirW, MAX_PATH);
273 if (p->file != USE_BUFF) MultiByteToWideChar(CP_ACP, 0, p->file, -1, fileW, MAX_PATH);
274 if (p->ext != USE_BUFF) MultiByteToWideChar(CP_ACP, 0, p->ext, -1, extW, MAX_PATH);
275
276 memset(buffer, 0, MAX_PATH);
277 for (n = 0; n < MAX_PATH; ++n)
278 bufferW[n] = 'X';
279 if (p->buffer) MultiByteToWideChar( CP_ACP, 0, p->buffer, -1, bufferW, MAX_PATH);
280
281 ret = p_wmakepath_s(bufferW, MAX_PATH,
282 p->drive == USE_BUFF ? bufferW : p->drive ? driveW : NULL,
283 p->dir == USE_BUFF ? bufferW : p->dir ? dirW : NULL,
284 p->file == USE_BUFF? bufferW : p->file ? fileW : NULL,
285 p->ext == USE_BUFF ? bufferW : p->ext ? extW : NULL);
286 ok(ret == 0, "[%d] Expected _wmakepath_s to return 0, got %d\n", i, ret);
287
288 bufferW[MAX_PATH - 1] = '\0';
289 WideCharToMultiByte(CP_ACP, 0, bufferW, -1, buffer, MAX_PATH, NULL, NULL);
290 ok(!strcmp(p->expected, buffer), "got '%s' for unicode case %d\n", buffer, i);
291 }
292
293 /* Try insufficient length cases. */
294 for (i = 0; i < sizeof(makepath_s_cases)/sizeof(makepath_s_cases[0]); i++)
295 {
296 const makepath_s_case *p = makepath_s_cases + i;
297
298 memset(buffer, 'X', MAX_PATH);
299 if (p->buffer)
300 strcpy(buffer, p->buffer);
301
302 /* Ascii */
303 errno = EBADF;
304 ret = p_makepath_s(buffer, p->length,
305 p->drive == USE_BUFF ? buffer : p->drive,
306 p->dir == USE_BUFF ? buffer : p->dir,
307 p->file == USE_BUFF? buffer : p->file,
308 p->ext == USE_BUFF ? buffer : p->ext);
309 ok(ret == ERANGE, "[%d] Expected _makepath_s to return ERANGE, got %d\n", i, ret);
310 ok(errno == ERANGE, "[%d] Expected errno to be ERANGE, got %d\n", i, errno);
311 ok(!memcmp(p->expected, buffer, p->expected_length), "unexpected output for case %d\n", i);
312
313 /* Unicode */
314 if (p->drive != USE_BUFF) MultiByteToWideChar(CP_ACP, 0, p->drive, -1, driveW, MAX_PATH);
315 if (p->dir != USE_BUFF) MultiByteToWideChar(CP_ACP, 0, p->dir, -1, dirW, MAX_PATH);
316 if (p->file != USE_BUFF) MultiByteToWideChar(CP_ACP, 0, p->file, -1, fileW, MAX_PATH);
317 if (p->ext != USE_BUFF) MultiByteToWideChar(CP_ACP, 0, p->ext, -1, extW, MAX_PATH);
318
319 memset(buffer, 0, MAX_PATH);
320 for (n = 0; n < MAX_PATH; ++n)
321 bufferW[n] = 'X';
322 if (p->buffer) MultiByteToWideChar( CP_ACP, 0, p->buffer, -1, bufferW, MAX_PATH);
323
324 errno = EBADF;
325 ret = p_wmakepath_s(bufferW, p->length,
326 p->drive == USE_BUFF ? bufferW : p->drive ? driveW : NULL,
327 p->dir == USE_BUFF ? bufferW : p->dir ? dirW : NULL,
328 p->file == USE_BUFF? bufferW : p->file ? fileW : NULL,
329 p->ext == USE_BUFF ? bufferW : p->ext ? extW : NULL);
330 ok(ret == ERANGE, "[%d] Expected _wmakepath_s to return ERANGE, got %d\n", i, ret);
331 ok(errno == ERANGE, "[%d] Expected errno to be ERANGE, got %d\n", i, errno);
332
333 ok(!memcmp(p->expected_unicode, bufferW, p->expected_length * sizeof(WCHAR)), "unexpected output for case %d\n", i);
334 }
335 }
336
337 static void test_fullpath(void)
338 {
339 char full[MAX_PATH];
340 char tmppath[MAX_PATH];
341 char prevpath[MAX_PATH];
342 char level1[MAX_PATH];
343 char level2[MAX_PATH];
344 char teststring[MAX_PATH];
345 char *freeme;
346 BOOL rc,free1,free2;
347
348 free1=free2=TRUE;
349 GetCurrentDirectoryA(MAX_PATH, prevpath);
350 GetTempPathA(MAX_PATH,tmppath);
351 strcpy(level1,tmppath);
352 strcat(level1,"msvcrt-test\\");
353
354 rc = CreateDirectoryA(level1,NULL);
355 if (!rc && GetLastError()==ERROR_ALREADY_EXISTS)
356 free1=FALSE;
357
358 strcpy(level2,level1);
359 strcat(level2,"nextlevel\\");
360 rc = CreateDirectoryA(level2,NULL);
361 if (!rc && GetLastError()==ERROR_ALREADY_EXISTS)
362 free2=FALSE;
363 SetCurrentDirectoryA(level2);
364
365 ok(_fullpath(full,"test", MAX_PATH)!=NULL,"_fullpath failed\n");
366 strcpy(teststring,level2);
367 strcat(teststring,"test");
368 ok(strcmp(full,teststring)==0,"Invalid Path returned %s\n",full);
369 ok(_fullpath(full,"\\test", MAX_PATH)!=NULL,"_fullpath failed\n");
370 memcpy(teststring,level2,3);
371 teststring[3]=0;
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 strcpy(teststring,level1);
376 strcat(teststring,"test");
377 ok(strcmp(full,teststring)==0,"Invalid Path returned %s\n",full);
378 ok(_fullpath(full,"..\\test", 10)==NULL,"_fullpath failed to generate error\n");
379
380 freeme = _fullpath(NULL,"test", 0);
381 ok(freeme!=NULL,"No path returned\n");
382 strcpy(teststring,level2);
383 strcat(teststring,"test");
384 ok(strcmp(freeme,teststring)==0,"Invalid Path returned %s\n",freeme);
385 free(freeme);
386
387 SetCurrentDirectoryA(prevpath);
388 if (free2)
389 RemoveDirectoryA(level2);
390 if (free1)
391 RemoveDirectoryA(level1);
392 }
393
394 static void test_splitpath(void)
395 {
396 const char* path = "c:\\\x83\x5c\x83\x74\x83\x67.bin";
397 char drive[3], dir[MAX_PATH], fname[MAX_PATH], ext[MAX_PATH];
398 int prev_cp = _getmbcp();
399
400 /* SBCS codepage */
401 _setmbcp(1252);
402 _splitpath(path, drive, dir, fname, ext);
403 ok(!strcmp(drive, "c:"), "got %s\n", drive);
404 ok(!strcmp(dir, "\\\x83\x5c"), "got %s\n", dir);
405 ok(!strcmp(fname, "\x83\x74\x83\x67"), "got %s\n", fname);
406 ok(!strcmp(ext, ".bin"), "got %s\n", ext);
407
408 /* MBCS (Japanese) codepage */
409 _setmbcp(932);
410 _splitpath(path, drive, dir, fname, ext);
411 ok(!strcmp(drive, "c:"), "got %s\n", drive);
412 ok(!strcmp(dir, "\\"), "got %s\n", dir);
413 ok(!strcmp(fname, "\x83\x5c\x83\x74\x83\x67"), "got %s\n", fname);
414 ok(!strcmp(ext, ".bin"), "got %s\n", ext);
415
416 _setmbcp(prev_cp);
417 }
418
419 START_TEST(dir)
420 {
421 init();
422
423 test_fullpath();
424 test_makepath();
425 test_makepath_s();
426 test_splitpath();
427 }