[win32, io] win32 passes files io test with new common io_open_at implementation
This commit is contained in:
parent
a64c86ca4e
commit
debcffce2a
|
@ -17,8 +17,9 @@
|
||||||
#if PLATFORM_WINDOWS
|
#if PLATFORM_WINDOWS
|
||||||
#include"platform/win32_memory.c"
|
#include"platform/win32_memory.c"
|
||||||
#include"platform/win32_clock.c"
|
#include"platform/win32_clock.c"
|
||||||
#include"platform/win32_io.c"
|
#include"platform/win32_string_helpers.c"
|
||||||
#include"platform/win32_path.c"
|
#include"platform/win32_path.c"
|
||||||
|
#include"platform/win32_io.c"
|
||||||
//TODO
|
//TODO
|
||||||
#elif PLATFORM_MACOS
|
#elif PLATFORM_MACOS
|
||||||
#include"platform/unix_memory.c"
|
#include"platform/unix_memory.c"
|
||||||
|
|
|
@ -109,6 +109,7 @@ io_open_restrict_result io_open_restrict(io_file_desc dirFd, str8 path, file_acc
|
||||||
|
|
||||||
str8_list sep = {0};
|
str8_list sep = {0};
|
||||||
str8_list_push(scratch.arena, &sep, STR8("/"));
|
str8_list_push(scratch.arena, &sep, STR8("/"));
|
||||||
|
str8_list_push(scratch.arena, &sep, STR8("\\"));
|
||||||
str8_list pathElements = str8_split(scratch.arena, path, sep);
|
str8_list pathElements = str8_split(scratch.arena, path, sep);
|
||||||
|
|
||||||
io_open_restrict_context context = {
|
io_open_restrict_context context = {
|
||||||
|
@ -277,7 +278,7 @@ io_cmp io_open_at(file_slot* atSlot, io_req* req, file_table* table)
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
slot->fd = -1;
|
slot->fd = io_file_desc_nil();
|
||||||
cmp.handle = file_handle_from_slot(table, slot);
|
cmp.handle = file_handle_from_slot(table, slot);
|
||||||
|
|
||||||
|
|
||||||
|
@ -324,6 +325,16 @@ io_cmp io_open_at(file_slot* atSlot, io_req* req, file_table* table)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if(slot->error == IO_OK)
|
||||||
|
{
|
||||||
|
file_status status;
|
||||||
|
slot->error = io_raw_fstat(slot->fd, &status);
|
||||||
|
if(slot->error == IO_OK)
|
||||||
|
{
|
||||||
|
slot->type = status.type;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if(slot->error)
|
if(slot->error)
|
||||||
{
|
{
|
||||||
slot->fatal = true;
|
slot->fatal = true;
|
||||||
|
|
|
@ -26,6 +26,7 @@ typedef struct file_slot
|
||||||
bool fatal;
|
bool fatal;
|
||||||
list_elt freeListElt;
|
list_elt freeListElt;
|
||||||
|
|
||||||
|
file_type type;
|
||||||
file_access_rights rights;
|
file_access_rights rights;
|
||||||
io_file_desc fd;
|
io_file_desc fd;
|
||||||
|
|
||||||
|
|
|
@ -11,6 +11,7 @@
|
||||||
#include<shlwapi.h>
|
#include<shlwapi.h>
|
||||||
#include<winioctl.h>
|
#include<winioctl.h>
|
||||||
|
|
||||||
|
#include"win32_string_helpers.h"
|
||||||
#include"platform_io_internal.c"
|
#include"platform_io_internal.c"
|
||||||
#include"platform_io_common.c"
|
#include"platform_io_common.c"
|
||||||
|
|
||||||
|
@ -72,25 +73,6 @@ io_error io_raw_last_error()
|
||||||
return(error);
|
return(error);
|
||||||
}
|
}
|
||||||
|
|
||||||
str16 win32_utf8_to_wide_null_terminated(mem_arena* arena, str8 s)
|
|
||||||
{
|
|
||||||
str16 res = {0};
|
|
||||||
res.len = 1 + MultiByteToWideChar(CP_UTF8, 0, s.ptr, s.len, NULL, 0);
|
|
||||||
res.ptr = mem_arena_alloc_array(arena, u16, res.len);
|
|
||||||
MultiByteToWideChar(CP_UTF8, 0, s.ptr, s.len, res.ptr, res.len);
|
|
||||||
res.ptr[res.len-1] = '\0';
|
|
||||||
return(res);
|
|
||||||
}
|
|
||||||
|
|
||||||
str8 win32_wide_to_utf8(mem_arena* arena, str16 s)
|
|
||||||
{
|
|
||||||
str8 res = {0};
|
|
||||||
res.len = WideCharToMultiByte(CP_UTF8, 0, s.ptr, s.len, NULL, 0, NULL, NULL);
|
|
||||||
res.ptr = mem_arena_alloc_array(arena, u8, res.len);
|
|
||||||
WideCharToMultiByte(CP_UTF8, 0, s.ptr, s.len, res.ptr, res.len, NULL, NULL);
|
|
||||||
return(res);
|
|
||||||
}
|
|
||||||
|
|
||||||
str16 win32_path_from_handle_null_terminated(mem_arena* arena, HANDLE handle)
|
str16 win32_path_from_handle_null_terminated(mem_arena* arena, HANDLE handle)
|
||||||
{
|
{
|
||||||
str16 res = {0};
|
str16 res = {0};
|
||||||
|
@ -120,6 +102,40 @@ bool io_file_desc_is_nil(io_file_desc fd)
|
||||||
return(fd == NULL || fd == INVALID_HANDLE_VALUE);
|
return(fd == NULL || fd == INVALID_HANDLE_VALUE);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
str16 win32_get_path_at_null_terminated(mem_arena* arena, io_file_desc dirFd, str8 path)
|
||||||
|
{
|
||||||
|
str16 result = {0};
|
||||||
|
mem_arena_scope scratch = mem_scratch_begin_next(arena);
|
||||||
|
|
||||||
|
str16 dirPathW = win32_path_from_handle_null_terminated(scratch.arena, dirFd);
|
||||||
|
str16 pathW = win32_utf8_to_wide_null_terminated(scratch.arena, path);
|
||||||
|
|
||||||
|
if(dirPathW.len && pathW.len)
|
||||||
|
{
|
||||||
|
u64 fullPathWSize = dirPathW.len + pathW.len;
|
||||||
|
LPWSTR fullPathW = mem_arena_alloc_array(scratch.arena, u16, fullPathWSize);
|
||||||
|
memcpy(fullPathW, dirPathW.ptr, (dirPathW.len-1)*sizeof(u16));
|
||||||
|
fullPathW[dirPathW.len-1] = '\\';
|
||||||
|
memcpy(fullPathW + dirPathW.len, pathW.ptr, pathW.len*sizeof(u16));
|
||||||
|
|
||||||
|
result.len = fullPathWSize;
|
||||||
|
result.ptr = mem_arena_alloc_array(arena, wchar_t, result.len);
|
||||||
|
|
||||||
|
if(PathCanonicalizeW(result.ptr, fullPathW))
|
||||||
|
{
|
||||||
|
result.len = lstrlenW(result.ptr);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
result.ptr = 0;
|
||||||
|
result.len = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
mem_scratch_end(scratch);
|
||||||
|
|
||||||
|
return(result);
|
||||||
|
}
|
||||||
|
|
||||||
io_file_desc io_raw_open_at(io_file_desc dirFd, str8 path, file_access_rights accessRights, file_open_flags openFlags)
|
io_file_desc io_raw_open_at(io_file_desc dirFd, str8 path, file_access_rights accessRights, file_open_flags openFlags)
|
||||||
{
|
{
|
||||||
HANDLE handle = INVALID_HANDLE_VALUE;
|
HANDLE handle = INVALID_HANDLE_VALUE;
|
||||||
|
@ -186,20 +202,10 @@ io_file_desc io_raw_open_at(io_file_desc dirFd, str8 path, file_access_rights ac
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
str16 dirPathW = win32_path_from_handle_null_terminated(scratch.arena, dirFd);
|
str16 fullPathW = win32_get_path_at_null_terminated(scratch.arena, dirFd, path);
|
||||||
|
if(fullPathW.len)
|
||||||
if(dirPathW.len && pathW.len)
|
|
||||||
{
|
{
|
||||||
u64 fullPathWSize = dirPathW.len + pathW.len;
|
handle = CreateFileW(fullPathW.ptr, win32AccessFlags, win32ShareMode, NULL, win32CreateFlags, win32AttributeFlags, NULL);
|
||||||
LPWSTR fullPathW = mem_arena_alloc_array(scratch.arena, u16, fullPathWSize);
|
|
||||||
memcpy(fullPathW, dirPathW.ptr, (dirPathW.len-1)*sizeof(u16));
|
|
||||||
fullPathW[dirPathW.len-1] = '\\';
|
|
||||||
memcpy(fullPathW + dirPathW.len, pathW.ptr, pathW.len*sizeof(u16));
|
|
||||||
|
|
||||||
LPWSTR canonical = mem_arena_alloc_array(scratch.arena, wchar_t, fullPathWSize);
|
|
||||||
PathCanonicalizeW(canonical, fullPathW);
|
|
||||||
|
|
||||||
handle = CreateFileW(canonical, win32AccessFlags, win32ShareMode, NULL, win32CreateFlags, win32AttributeFlags, NULL);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
mem_scratch_end(scratch);
|
mem_scratch_end(scratch);
|
||||||
|
@ -211,7 +217,19 @@ void io_raw_close(io_file_desc fd)
|
||||||
CloseHandle(fd);
|
CloseHandle(fd);
|
||||||
}
|
}
|
||||||
|
|
||||||
io_error io_raw_stat(io_file_desc fd, file_status* status)
|
bool io_raw_file_exists_at(io_file_desc dirFd, str8 path, file_open_flags openFlags)
|
||||||
|
{
|
||||||
|
bool result = false;
|
||||||
|
io_file_desc fd = io_raw_open_at(dirFd, path, FILE_ACCESS_NONE, (openFlags & FILE_OPEN_SYMLINK));
|
||||||
|
if(!io_file_desc_is_nil(fd))
|
||||||
|
{
|
||||||
|
result = true;
|
||||||
|
io_raw_close(fd);
|
||||||
|
}
|
||||||
|
return(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
io_error io_raw_fstat(io_file_desc fd, file_status* status)
|
||||||
{
|
{
|
||||||
io_error error = IO_OK;
|
io_error error = IO_OK;
|
||||||
|
|
||||||
|
@ -277,35 +295,22 @@ io_error io_raw_stat(io_file_desc fd, file_status* status)
|
||||||
return(error);
|
return(error);
|
||||||
}
|
}
|
||||||
|
|
||||||
u64 io_raw_uid(io_file_desc fd)
|
io_error io_raw_fstat_at(io_file_desc dirFd, str8 name, file_open_flags openFlags, file_status* status)
|
||||||
{
|
|
||||||
file_status status;
|
|
||||||
io_raw_stat(fd, &status);
|
|
||||||
return(status.uid);
|
|
||||||
}
|
|
||||||
|
|
||||||
io_error io_raw_stat_at(io_file_desc dirFd, str8 name, file_open_flags openFlags, file_status* status)
|
|
||||||
{
|
{
|
||||||
io_error error = IO_OK;
|
io_error error = IO_OK;
|
||||||
io_file_desc fd = io_raw_open_at(dirFd, name, FILE_ACCESS_READ, FILE_OPEN_SYMLINK);
|
io_file_desc fd = io_raw_open_at(dirFd, name, FILE_ACCESS_NONE, FILE_OPEN_SYMLINK);
|
||||||
if(io_file_desc_is_nil(fd))
|
if(io_file_desc_is_nil(fd))
|
||||||
{
|
{
|
||||||
error = io_raw_last_error();
|
error = io_raw_last_error();
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
error = io_raw_stat(fd, status);
|
error = io_raw_fstat(fd, status);
|
||||||
io_raw_close(fd);
|
io_raw_close(fd);
|
||||||
}
|
}
|
||||||
return(error);
|
return(error);
|
||||||
}
|
}
|
||||||
|
|
||||||
typedef struct io_raw_read_link_result
|
|
||||||
{
|
|
||||||
io_error error;
|
|
||||||
str8 target;
|
|
||||||
} io_raw_read_link_result;
|
|
||||||
|
|
||||||
typedef struct
|
typedef struct
|
||||||
{
|
{
|
||||||
ULONG ReparseTag;
|
ULONG ReparseTag;
|
||||||
|
@ -376,166 +381,6 @@ io_raw_read_link_result io_raw_read_link_at(mem_arena* arena, io_file_desc dirFd
|
||||||
return(result);
|
return(result);
|
||||||
}
|
}
|
||||||
|
|
||||||
typedef struct io_open_restrict_context
|
|
||||||
{
|
|
||||||
io_error error;
|
|
||||||
u64 rootUID;
|
|
||||||
io_file_desc rootFd;
|
|
||||||
io_file_desc fd;
|
|
||||||
|
|
||||||
} io_open_restrict_context;
|
|
||||||
|
|
||||||
io_error io_open_restrict_enter(io_open_restrict_context* context, str8 name, file_access_rights accessRights, file_open_flags openFlags)
|
|
||||||
{
|
|
||||||
io_file_desc nextFd = io_raw_open_at(context->fd, name, accessRights, openFlags);
|
|
||||||
if(io_file_desc_is_nil(nextFd))
|
|
||||||
{
|
|
||||||
context->error = io_raw_last_error();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if(context->fd != context->rootFd)
|
|
||||||
{
|
|
||||||
io_raw_close(context->fd);
|
|
||||||
}
|
|
||||||
context->fd = nextFd;
|
|
||||||
}
|
|
||||||
return(context->error);
|
|
||||||
}
|
|
||||||
|
|
||||||
typedef struct io_open_restrict_result
|
|
||||||
{
|
|
||||||
io_error error;
|
|
||||||
HANDLE h;
|
|
||||||
} io_open_restrict_result;
|
|
||||||
|
|
||||||
|
|
||||||
io_open_restrict_result io_open_path_restrict(io_file_desc dirFd, str8 path, file_access_rights accessRights, file_open_flags openFlags)
|
|
||||||
{
|
|
||||||
mem_arena_scope scratch = mem_scratch_begin();
|
|
||||||
|
|
||||||
str8_list sep = {0};
|
|
||||||
str8_list_push(scratch.arena, &sep, STR8("/"));
|
|
||||||
str8_list pathElements = str8_split(scratch.arena, path, sep);
|
|
||||||
|
|
||||||
io_open_restrict_context context = {
|
|
||||||
.error = IO_OK,
|
|
||||||
.rootFd = dirFd,
|
|
||||||
.rootUID = io_raw_uid(dirFd),
|
|
||||||
.fd = dirFd,
|
|
||||||
};
|
|
||||||
|
|
||||||
for_list(&pathElements.list, elt, str8_elt, listElt)
|
|
||||||
{
|
|
||||||
str8 name = elt->string;
|
|
||||||
|
|
||||||
if(!str8_cmp(name, STR8(".")))
|
|
||||||
{
|
|
||||||
//NOTE: skip;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
else if(!str8_cmp(name, STR8("..")))
|
|
||||||
{
|
|
||||||
//NOTE: check that we don't escape root dir
|
|
||||||
if(io_raw_uid(context.fd) == context.rootUID)
|
|
||||||
{
|
|
||||||
context.error = IO_ERR_WALKOUT;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
io_open_restrict_enter(&context, name, FILE_ACCESS_READ, 0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
file_status status = {0};
|
|
||||||
context.error = io_raw_stat_at(context.fd, name, FILE_OPEN_SYMLINK, &status);
|
|
||||||
if(context.error)
|
|
||||||
{
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(status.type == MP_FILE_SYMLINK)
|
|
||||||
{
|
|
||||||
//NOTE: read link target and add to file
|
|
||||||
io_raw_read_link_result link = io_raw_read_link_at(scratch.arena, context.fd, name);
|
|
||||||
|
|
||||||
if(link.error)
|
|
||||||
{
|
|
||||||
context.error = link.error;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(link.target.len == 0)
|
|
||||||
{
|
|
||||||
// skip
|
|
||||||
}
|
|
||||||
else if( link.target.ptr[0] == '/'
|
|
||||||
|| link.target.ptr[0] == '\\')
|
|
||||||
{
|
|
||||||
context.error = IO_ERR_WALKOUT;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
str8_list linkElements = str8_split(scratch.arena, link.target, sep);
|
|
||||||
if(!list_empty(&linkElements.list))
|
|
||||||
{
|
|
||||||
//NOTE: insert linkElements into pathElements after elt
|
|
||||||
list_elt* tmp = elt->listElt.next;
|
|
||||||
elt->listElt.next = linkElements.list.first;
|
|
||||||
linkElements.list.last->next = tmp;
|
|
||||||
if(!tmp)
|
|
||||||
{
|
|
||||||
pathElements.list.last = linkElements.list.last;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if(status.type == MP_FILE_DIRECTORY)
|
|
||||||
{
|
|
||||||
//NOTE: descend in directory
|
|
||||||
io_open_restrict_enter(&context, name, FILE_ACCESS_READ, 0);
|
|
||||||
}
|
|
||||||
else if(status.type == MP_FILE_REGULAR)
|
|
||||||
{
|
|
||||||
//NOTE: check that we're at the end of path and open that file
|
|
||||||
if(&elt->listElt != list_last(&pathElements.list))
|
|
||||||
{
|
|
||||||
context.error = IO_ERR_NOT_DIR;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
io_open_restrict_enter(&context, name, accessRights, openFlags);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
context.error = IO_ERR_NO_ENTRY;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if(context.error)
|
|
||||||
{
|
|
||||||
if(context.fd != context.rootFd)
|
|
||||||
{
|
|
||||||
io_raw_close(context.fd);
|
|
||||||
context.fd = io_file_desc_nil();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
io_open_restrict_result result = {
|
|
||||||
.error = context.error,
|
|
||||||
.h = context.fd
|
|
||||||
};
|
|
||||||
|
|
||||||
mem_scratch_end(scratch);
|
|
||||||
return(result);
|
|
||||||
}
|
|
||||||
|
|
||||||
io_cmp io_close(file_slot* slot, io_req* req, file_table* table)
|
io_cmp io_close(file_slot* slot, io_req* req, file_table* table)
|
||||||
{
|
{
|
||||||
io_cmp cmp = {0};
|
io_cmp cmp = {0};
|
||||||
|
@ -557,7 +402,7 @@ io_cmp io_fstat(file_slot* slot, io_req* req)
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
slot->error = io_raw_stat(slot->fd, (file_status*)req->buffer);
|
slot->error = io_raw_fstat(slot->fd, (file_status*)req->buffer);
|
||||||
cmp.error = slot->error;
|
cmp.error = slot->error;
|
||||||
}
|
}
|
||||||
return(cmp);
|
return(cmp);
|
||||||
|
@ -602,6 +447,13 @@ io_cmp io_read(file_slot* slot, io_req* req)
|
||||||
{
|
{
|
||||||
io_cmp cmp = {0};
|
io_cmp cmp = {0};
|
||||||
|
|
||||||
|
if(slot->type != MP_FILE_REGULAR)
|
||||||
|
{
|
||||||
|
slot->error = IO_ERR_PERM;
|
||||||
|
cmp.error = slot->error;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
DWORD bytesRead = 0;
|
DWORD bytesRead = 0;
|
||||||
|
|
||||||
if(!ReadFile(slot->fd, req->buffer, req->size, &bytesRead, NULL))
|
if(!ReadFile(slot->fd, req->buffer, req->size, &bytesRead, NULL))
|
||||||
|
@ -614,6 +466,7 @@ io_cmp io_read(file_slot* slot, io_req* req)
|
||||||
{
|
{
|
||||||
cmp.result = bytesRead;
|
cmp.result = bytesRead;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
return(cmp);
|
return(cmp);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -621,6 +474,13 @@ io_cmp io_write(file_slot* slot, io_req* req)
|
||||||
{
|
{
|
||||||
io_cmp cmp = {0};
|
io_cmp cmp = {0};
|
||||||
|
|
||||||
|
if(slot->type != MP_FILE_REGULAR)
|
||||||
|
{
|
||||||
|
slot->error = IO_ERR_PERM;
|
||||||
|
cmp.error = slot->error;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
DWORD bytesWritten = 0;
|
DWORD bytesWritten = 0;
|
||||||
|
|
||||||
if(!WriteFile(slot->fd, req->buffer, req->size, &bytesWritten, NULL))
|
if(!WriteFile(slot->fd, req->buffer, req->size, &bytesWritten, NULL))
|
||||||
|
@ -633,7 +493,7 @@ io_cmp io_write(file_slot* slot, io_req* req)
|
||||||
{
|
{
|
||||||
cmp.result = bytesWritten;
|
cmp.result = bytesWritten;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
return(cmp);
|
return(cmp);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -5,9 +5,21 @@
|
||||||
* @date: 24/05/2023
|
* @date: 24/05/2023
|
||||||
*
|
*
|
||||||
*****************************************************************/
|
*****************************************************************/
|
||||||
|
#include<shlwapi.h> // PathIsRelative()
|
||||||
|
|
||||||
|
#include"win32_string_helpers.h"
|
||||||
#include"platform_path.c"
|
#include"platform_path.c"
|
||||||
|
|
||||||
|
bool path_is_absolute(str8 path)
|
||||||
|
{
|
||||||
|
mem_arena_scope scratch = mem_scratch_begin();
|
||||||
|
str16 pathW = win32_utf8_to_wide_null_terminated(scratch.arena, path);
|
||||||
|
bool result = !PathIsRelativeW(pathW.ptr);
|
||||||
|
|
||||||
|
mem_scratch_end(scratch);
|
||||||
|
return(result);
|
||||||
|
}
|
||||||
|
|
||||||
str8 path_executable(mem_arena* arena)
|
str8 path_executable(mem_arena* arena)
|
||||||
{
|
{
|
||||||
//TODO use wide chars
|
//TODO use wide chars
|
||||||
|
|
|
@ -0,0 +1,31 @@
|
||||||
|
/************************************************************//**
|
||||||
|
*
|
||||||
|
* @file: win32_string_helpers.c
|
||||||
|
* @author: Martin Fouilleul
|
||||||
|
* @date: 24/05/2023
|
||||||
|
*
|
||||||
|
*****************************************************************/
|
||||||
|
|
||||||
|
#define WIN32_LEAN_AND_MEAN
|
||||||
|
#include<windows.h>
|
||||||
|
|
||||||
|
#include"win32_string_helpers.h"
|
||||||
|
|
||||||
|
str16 win32_utf8_to_wide_null_terminated(mem_arena* arena, str8 s)
|
||||||
|
{
|
||||||
|
str16 res = {0};
|
||||||
|
res.len = 1 + MultiByteToWideChar(CP_UTF8, 0, s.ptr, s.len, NULL, 0);
|
||||||
|
res.ptr = mem_arena_alloc_array(arena, u16, res.len);
|
||||||
|
MultiByteToWideChar(CP_UTF8, 0, s.ptr, s.len, res.ptr, res.len);
|
||||||
|
res.ptr[res.len-1] = '\0';
|
||||||
|
return(res);
|
||||||
|
}
|
||||||
|
|
||||||
|
str8 win32_wide_to_utf8(mem_arena* arena, str16 s)
|
||||||
|
{
|
||||||
|
str8 res = {0};
|
||||||
|
res.len = WideCharToMultiByte(CP_UTF8, 0, s.ptr, s.len, NULL, 0, NULL, NULL);
|
||||||
|
res.ptr = mem_arena_alloc_array(arena, u8, res.len);
|
||||||
|
WideCharToMultiByte(CP_UTF8, 0, s.ptr, s.len, res.ptr, res.len, NULL, NULL);
|
||||||
|
return(res);
|
||||||
|
}
|
|
@ -0,0 +1,17 @@
|
||||||
|
/************************************************************//**
|
||||||
|
*
|
||||||
|
* @file: win32_string_helpers.h
|
||||||
|
* @author: Martin Fouilleul
|
||||||
|
* @date: 24/05/2023
|
||||||
|
*
|
||||||
|
*****************************************************************/
|
||||||
|
#ifndef __WIN32_STRING_HELPERS_H_
|
||||||
|
#define __WIN32_STRING_HELPERS_H_
|
||||||
|
|
||||||
|
#include"util/strings.h"
|
||||||
|
|
||||||
|
str16 win32_utf8_to_wide_null_terminated(mem_arena* arena, str8 s);
|
||||||
|
str8 win32_wide_to_utf8(mem_arena* arena, str16 s);
|
||||||
|
|
||||||
|
|
||||||
|
#endif // __WIN32_STRING_HELPERS_H_
|
Loading…
Reference in New Issue