diff --git a/dlls/msvcr100/msvcr100.spec b/dlls/msvcr100/msvcr100.spec index 99730e5a130..e0783880398 100644 --- a/dlls/msvcr100/msvcr100.spec +++ b/dlls/msvcr100/msvcr100.spec @@ -819,7 +819,7 @@ @ cdecl _ltow(long ptr long) msvcrt._ltow @ stub _ltow_s @ cdecl _makepath(ptr str str str str) msvcrt._makepath -@ stub _makepath_s +@ cdecl _makepath_s(ptr long str str str str) msvcrt._makepath_s @ cdecl _malloc_crt(long) msvcrt.malloc @ cdecl _mbbtombc(long) msvcrt._mbbtombc @ stub _mbbtombc_l diff --git a/dlls/msvcr80/msvcr80.spec b/dlls/msvcr80/msvcr80.spec index 8f3769e7027..07b590e59a7 100644 --- a/dlls/msvcr80/msvcr80.spec +++ b/dlls/msvcr80/msvcr80.spec @@ -658,7 +658,7 @@ @ cdecl _ltow(long ptr long) msvcrt._ltow @ stub _ltow_s @ cdecl _makepath(ptr str str str str) msvcrt._makepath -@ stub _makepath_s +@ cdecl _makepath_s(ptr long str str str str) msvcrt._makepath_s @ cdecl _malloc_crt(long) msvcrt.malloc @ cdecl _mbbtombc(long) msvcrt._mbbtombc @ stub _mbbtombc_l diff --git a/dlls/msvcr90/msvcr90.spec b/dlls/msvcr90/msvcr90.spec index e00a331db12..63077474259 100644 --- a/dlls/msvcr90/msvcr90.spec +++ b/dlls/msvcr90/msvcr90.spec @@ -646,7 +646,7 @@ @ cdecl _ltow(long ptr long) msvcrt._ltow @ stub _ltow_s @ cdecl _makepath(ptr str str str str) msvcrt._makepath -@ stub _makepath_s +@ cdecl _makepath_s(ptr long str str str str) msvcrt._makepath_s @ cdecl _malloc_crt(long) msvcrt.malloc @ cdecl _mbbtombc(long) msvcrt._mbbtombc @ stub _mbbtombc_l diff --git a/dlls/msvcrt/dir.c b/dlls/msvcrt/dir.c index a9f9234d604..e3862f3ad20 100644 --- a/dlls/msvcrt/dir.c +++ b/dlls/msvcrt/dir.c @@ -1077,6 +1077,110 @@ VOID CDECL _wmakepath(MSVCRT_wchar_t *path, const MSVCRT_wchar_t *drive, const M TRACE("returning %s\n", debugstr_w(path)); } +/********************************************************************* + * _makepath_s (MSVCRT.@) + * + * Safe version of _makepath. + */ +int CDECL _makepath_s(char *path, MSVCRT_size_t size, const char *drive, + const char *directory, const char *filename, + const char *extension) +{ + char *p = path; + + if (!path || !size) + { + *MSVCRT__errno() = MSVCRT_EINVAL; + return MSVCRT_EINVAL; + } + + if (drive && drive[0]) + { + if (size <= 2) + goto range; + + *p++ = drive[0]; + *p++ = ':'; + size -= 2; + } + + if (directory && directory[0]) + { + unsigned int len = strlen(directory); + unsigned int needs_separator = directory[len - 1] != '/' && directory[len - 1] != '\\'; + unsigned int copylen = min(size - 1, len); + + if (size < 2) + goto range; + + memmove(p, directory, copylen); + + if (size <= len) + goto range; + + p += copylen; + size -= copylen; + + if (needs_separator) + { + if (size < 2) + goto range; + + *p++ = '\\'; + size -= 1; + } + } + + if (filename && filename[0]) + { + unsigned int len = strlen(filename); + unsigned int copylen = min(size - 1, len); + + if (size < 2) + goto range; + + memmove(p, filename, copylen); + + if (size <= len) + goto range; + + p += len; + size -= len; + } + + if (extension && extension[0]) + { + unsigned int len = strlen(extension); + unsigned int needs_period = extension[0] != '.'; + unsigned int copylen; + + if (size < 2) + goto range; + + if (needs_period) + { + *p++ = '.'; + size -= 1; + } + + copylen = min(size - 1, len); + memcpy(p, extension, copylen); + + if (size <= len) + goto range; + + p += copylen; + } + + *p = '\0'; + return 0; + +range: + path[0] = '\0'; + *MSVCRT__errno() = MSVCRT_ERANGE; + return MSVCRT_ERANGE; +} + /********************************************************************* * _searchenv (MSVCRT.@) * diff --git a/dlls/msvcrt/msvcrt.spec b/dlls/msvcrt/msvcrt.spec index e39253e1e32..2a760af5751 100644 --- a/dlls/msvcrt/msvcrt.spec +++ b/dlls/msvcrt/msvcrt.spec @@ -600,7 +600,7 @@ @ cdecl _ltow(long ptr long) ntdll._ltow # stub _ltow_s @ cdecl _makepath(ptr str str str str) -# stub _makepath_s +@ cdecl _makepath_s(ptr long str str str str) # stub _malloc_dbg @ cdecl _matherr(ptr) MSVCRT__matherr @ cdecl _mbbtombc(long) diff --git a/dlls/msvcrt/tests/dir.c b/dlls/msvcrt/tests/dir.c index 11a982093f2..98776c3753a 100644 --- a/dlls/msvcrt/tests/dir.c +++ b/dlls/msvcrt/tests/dir.c @@ -31,6 +31,15 @@ #include #include +static int (__cdecl *p_makepath_s)(char *, size_t, const char *, const char *, const char *, const char *); + +static void init(void) +{ + HMODULE hmod = GetModuleHandleA("msvcrt.dll"); + + p_makepath_s = (void *)GetProcAddress(hmod, "_makepath_s"); +} + typedef struct { const char* buffer; @@ -121,6 +130,117 @@ static void test_makepath(void) } } +typedef struct +{ + const char* buffer; + size_t length; + const char* drive; + const char* dir; + const char* file; + const char* ext; + const char* expected; + size_t expected_length; +} makepath_s_case; + +static const makepath_s_case makepath_s_cases[] = +{ + /* Behavior with directory parameter containing backslash. */ + {NULL, 1, "c:", "d\\", "file", "ext", "\0XXXXXXXXXXXX", 13}, + {NULL, 2, "c:", "d\\", "file", "ext", "\0XXXXXXXXXXXX", 13}, + {NULL, 3, "c:", "d\\", "file", "ext", "\0:XXXXXXXXXXX", 13}, + {NULL, 4, "c:", "d\\", "file", "ext", "\0:dXXXXXXXXXX", 13}, + {NULL, 5, "c:", "d\\", "file", "ext", "\0:d\\XXXXXXXXX", 13}, + {NULL, 6, "c:", "d\\", "file", "ext", "\0:d\\fXXXXXXXX", 13}, + {NULL, 7, "c:", "d\\", "file", "ext", "\0:d\\fiXXXXXXX", 13}, + {NULL, 8, "c:", "d\\", "file", "ext", "\0:d\\filXXXXXX", 13}, + {NULL, 9, "c:", "d\\", "file", "ext", "\0:d\\fileXXXXX", 13}, + {NULL, 10, "c:", "d\\", "file", "ext", "\0:d\\file.XXXX", 13}, + {NULL, 11, "c:", "d\\", "file", "ext", "\0:d\\file.eXXX", 13}, + {NULL, 12, "c:", "d\\", "file", "ext", "\0:d\\file.exXX", 13}, + /* Behavior with directory parameter lacking backslash. */ + {NULL, 3, "c:", "dir", "f", "ext", "\0:XXXXXXXX", 10}, + {NULL, 4, "c:", "dir", "f", "ext", "\0:dXXXXXXX", 10}, + {NULL, 5, "c:", "dir", "f", "ext", "\0:diXXXXXX", 10}, + {NULL, 6, "c:", "dir", "f", "ext", "\0:dirXXXXX", 10}, + {NULL, 7, "c:", "dir", "f", "ext", "\0:dir\\XXXX", 10}, + /* Behavior with overlapped buffer. */ + {"foo", 2, USE_BUFF, NULL, NULL, NULL, "\0oo", 3}, + {"foo", 4, NULL, USE_BUFF, NULL, NULL, "\0oo\0X", 5}, + {"foo", 3, NULL, NULL, USE_BUFF, NULL, "\0oo\0", 4}, + {"foo", 4, NULL, USE_BUFF, "file", NULL, "\0oo\0XXXXX", 9}, + {"foo", 8, NULL, USE_BUFF, "file", NULL, "\0oo\\filXX", 9}, + {"foo", 4, NULL, USE_BUFF, "file", "ext", "\0oo\0XXXXXXXXX", 13}, + {"foo", 8, NULL, USE_BUFF, "file", "ext", "\0oo\\filXXXXXX", 13}, + {"foo", 12, NULL, USE_BUFF, "file", "ext", "\0oo\\file.exXX", 13}, + {"foo", 4, NULL, NULL, USE_BUFF, "ext", "\0oo\0XXXX", 8}, + {"foo", 7, NULL, NULL, USE_BUFF, "ext", "\0oo.exXX", 8}, +}; + +static void test_makepath_s(void) +{ + char buffer[MAX_PATH]; + int ret; + unsigned int i; + + if (!p_makepath_s) + { + win_skip("_makepath_s is not available\n"); + return; + } + + errno = EBADF; + ret = p_makepath_s(NULL, 0, NULL, NULL, NULL, NULL); + ok(ret == EINVAL, "Expected _makepath_s to return EINVAL, got %d\n", ret); + ok(errno == EINVAL, "Expected errno to be EINVAL, got %d\n", errno); + + errno = EBADF; + ret = p_makepath_s(buffer, 0, NULL, NULL, NULL, NULL); + ok(ret == EINVAL, "Expected _makepath_s to return EINVAL, got %d\n", ret); + ok(errno == EINVAL, "Expected errno to be EINVAL, got %d\n", errno); + + /* Test with the normal _makepath cases. */ + for (i = 0; i < sizeof(makepath_cases)/sizeof(makepath_cases[0]); i++) + { + const makepath_case *p = makepath_cases + i; + + memset(buffer, 'X', MAX_PATH); + if (p->buffer) + strcpy(buffer, p->buffer); + + /* Ascii */ + ret = p_makepath_s(buffer, MAX_PATH, + p->drive == USE_BUFF ? buffer : p->drive, + p->dir == USE_BUFF ? buffer : p->dir, + p->file == USE_BUFF? buffer : p->file, + p->ext == USE_BUFF ? buffer : p->ext); + ok(ret == 0, "[%d] Expected _makepath_s to return 0, got %d\n", i, ret); + + buffer[MAX_PATH - 1] = '\0'; + ok(!strcmp(p->expected, buffer), "got '%s' for case %d\n", buffer, i); + } + + /* Try insufficient length cases. */ + for (i = 0; i < sizeof(makepath_s_cases)/sizeof(makepath_s_cases[0]); i++) + { + const makepath_s_case *p = makepath_s_cases + i; + + memset(buffer, 'X', MAX_PATH); + if (p->buffer) + strcpy(buffer, p->buffer); + + /* Ascii */ + errno = EBADF; + ret = p_makepath_s(buffer, p->length, + p->drive == USE_BUFF ? buffer : p->drive, + p->dir == USE_BUFF ? buffer : p->dir, + p->file == USE_BUFF? buffer : p->file, + p->ext == USE_BUFF ? buffer : p->ext); + ok(ret == ERANGE, "[%d] Expected _makepath_s to return ERANGE, got %d\n", i, ret); + ok(errno == ERANGE, "[%d] Expected errno to be ERANGE, got %d\n", i, errno); + ok(!memcmp(p->expected, buffer, p->expected_length), "unexpected output for case %d\n", i); + } +} + static void test_fullpath(void) { char full[MAX_PATH]; @@ -180,6 +300,9 @@ static void test_fullpath(void) START_TEST(dir) { + init(); + test_fullpath(); test_makepath(); + test_makepath_s(); } diff --git a/include/msvcrt/stdlib.h b/include/msvcrt/stdlib.h index c6a3e0000e1..e7130b9471e 100644 --- a/include/msvcrt/stdlib.h +++ b/include/msvcrt/stdlib.h @@ -148,6 +148,7 @@ char* __cdecl _ltoa(__msvcrt_long,char*,int); __msvcrt_ulong __cdecl _lrotl(__msvcrt_ulong,int); __msvcrt_ulong __cdecl _lrotr(__msvcrt_ulong,int); void __cdecl _makepath(char*,const char*,const char*,const char*,const char*); +int __cdecl _makepath_s(char*,size_t,const char*,const char*,const char*,const char*); size_t __cdecl _mbstrlen(const char*); _onexit_t __cdecl _onexit(_onexit_t); int __cdecl _putenv(const char*);