msvcrt: Implement _makepath_s.
This commit is contained in:
parent
9edc384263
commit
9628a0d3e4
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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.@)
|
||||
*
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -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*);
|
||||
|
|
Loading…
Reference in New Issue