msvcrt: Implement _makepath_s.

This commit is contained in:
Andrew Nguyen 2010-06-25 03:53:41 -05:00 committed by Alexandre Julliard
parent 9edc384263
commit 9628a0d3e4
7 changed files with 232 additions and 4 deletions

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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.@)
*

View file

@ -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)

View file

@ -31,6 +31,15 @@
#include <process.h>
#include <errno.h>
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();
}

View file

@ -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*);