mirror of
https://github.com/Ed94/Odin.git
synced 2026-08-06 15:48:51 +00:00
Make the utf16 conversion procedures in core:sys/windows safer by checking for memory leaks
This commit is contained in:
@@ -13,7 +13,7 @@ read_dir :: proc(fd: Handle, n: int, allocator := context.allocator) -> (fi: []F
|
|||||||
if d.cFileName[0] == '.' && d.cFileName[1] == '.' && d.cFileName[2] == 0 {
|
if d.cFileName[0] == '.' && d.cFileName[1] == '.' && d.cFileName[2] == 0 {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
path := strings.concatenate({base_path, `\`, win32.utf16_to_utf8(d.cFileName[:])})
|
path := strings.concatenate({base_path, `\`, win32.utf16_to_utf8(d.cFileName[:]) or_else ""})
|
||||||
fi.fullpath = path
|
fi.fullpath = path
|
||||||
fi.name = basename(path)
|
fi.name = basename(path)
|
||||||
fi.size = i64(d.nFileSizeHigh)<<32 + i64(d.nFileSizeLow)
|
fi.size = i64(d.nFileSizeHigh)<<32 + i64(d.nFileSizeLow)
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ lookup_env :: proc(key: string, allocator := context.allocator) -> (value: strin
|
|||||||
}
|
}
|
||||||
|
|
||||||
if n <= u32(len(b)) {
|
if n <= u32(len(b)) {
|
||||||
value = win32.utf16_to_utf8(b[:n], allocator)
|
value, _ = win32.utf16_to_utf8(b[:n], allocator)
|
||||||
found = true
|
found = true
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -76,7 +76,7 @@ environ :: proc(allocator := context.allocator) -> []string {
|
|||||||
if i <= from {
|
if i <= from {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
append(&r, win32.utf16_to_utf8(envs[from:i], allocator))
|
append(&r, win32.utf16_to_utf8(envs[from:i], allocator) or_else "")
|
||||||
from = i + 1
|
from = i + 1
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -365,7 +365,7 @@ get_current_directory :: proc(allocator := context.allocator) -> string {
|
|||||||
|
|
||||||
win32.ReleaseSRWLockExclusive(&cwd_lock)
|
win32.ReleaseSRWLockExclusive(&cwd_lock)
|
||||||
|
|
||||||
return win32.utf16_to_utf8(dir_buf_wstr, allocator)
|
return win32.utf16_to_utf8(dir_buf_wstr, allocator) or_else ""
|
||||||
}
|
}
|
||||||
|
|
||||||
set_current_directory :: proc(path: string) -> (err: Errno) {
|
set_current_directory :: proc(path: string) -> (err: Errno) {
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ _lookup_env :: proc(key: string, allocator: runtime.Allocator) -> (value: string
|
|||||||
return "", false
|
return "", false
|
||||||
}
|
}
|
||||||
|
|
||||||
value = win32.utf16_to_utf8(b[:n], allocator)
|
value = win32.utf16_to_utf8(b[:n], allocator) or_else ""
|
||||||
found = true
|
found = true
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -73,7 +73,7 @@ _environ :: proc(allocator: runtime.Allocator) -> []string {
|
|||||||
break
|
break
|
||||||
}
|
}
|
||||||
w := ([^]u16)(p)[from:i]
|
w := ([^]u16)(p)[from:i]
|
||||||
append(&r, win32.utf16_to_utf8(w, allocator))
|
append(&r, win32.utf16_to_utf8(w, allocator) or_else "")
|
||||||
from = i + 1
|
from = i + 1
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ File_Mode_Device :: File_Mode(1<<18)
|
|||||||
File_Mode_Char_Device :: File_Mode(1<<19)
|
File_Mode_Char_Device :: File_Mode(1<<19)
|
||||||
File_Mode_Sym_Link :: File_Mode(1<<20)
|
File_Mode_Sym_Link :: File_Mode(1<<20)
|
||||||
|
|
||||||
|
File_Mode_Perm :: File_Mode(0o777) // Unix permision bits
|
||||||
|
|
||||||
File_Flags :: distinct bit_set[File_Flag; uint]
|
File_Flags :: distinct bit_set[File_Flag; uint]
|
||||||
File_Flag :: enum {
|
File_Flag :: enum {
|
||||||
@@ -194,3 +195,20 @@ is_dir :: proc(path: string) -> bool {
|
|||||||
return _is_dir(path)
|
return _is_dir(path)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
copy_file :: proc(dst_path, src_path: string) -> Error {
|
||||||
|
src := open(src_path) or_return
|
||||||
|
defer close(src)
|
||||||
|
|
||||||
|
info := fstat(src, _file_allocator()) or_return
|
||||||
|
defer file_info_delete(info, _file_allocator())
|
||||||
|
if info.is_dir {
|
||||||
|
return .Invalid_File
|
||||||
|
}
|
||||||
|
|
||||||
|
dst := open(dst_path, {.Read, .Write, .Create, .Trunc}, info.mode & File_Mode_Perm) or_return
|
||||||
|
defer close(dst)
|
||||||
|
|
||||||
|
_, err := io.copy(to_writer(dst), to_reader(src))
|
||||||
|
return err
|
||||||
|
}
|
||||||
@@ -2,12 +2,20 @@ package os2
|
|||||||
|
|
||||||
import "core:io"
|
import "core:io"
|
||||||
|
|
||||||
file_to_stream :: proc(f: ^File) -> (s: io.Stream) {
|
to_stream :: proc(f: ^File) -> (s: io.Stream) {
|
||||||
s.stream_data = f
|
s.stream_data = f
|
||||||
s.stream_vtable = _file_stream_vtable
|
s.stream_vtable = _file_stream_vtable
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
to_writer :: proc(f: ^File) -> (s: io.Writer) {
|
||||||
|
return {to_stream(f)}
|
||||||
|
}
|
||||||
|
to_reader :: proc(f: ^File) -> (s: io.Reader) {
|
||||||
|
return {to_stream(f)}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
@(private)
|
@(private)
|
||||||
error_to_io_error :: proc(ferr: Error) -> io.Error {
|
error_to_io_error :: proc(ferr: Error) -> io.Error {
|
||||||
if ferr == nil {
|
if ferr == nil {
|
||||||
|
|||||||
@@ -529,16 +529,16 @@ _normalize_link_path :: proc(p: []u16, allocator: runtime.Allocator) -> (str: st
|
|||||||
}
|
}
|
||||||
|
|
||||||
if !has_unc_prefix(p) {
|
if !has_unc_prefix(p) {
|
||||||
return win32.utf16_to_utf8(p, allocator), nil
|
return win32.utf16_to_utf8(p, allocator)
|
||||||
}
|
}
|
||||||
|
|
||||||
ws := p[4:]
|
ws := p[4:]
|
||||||
switch {
|
switch {
|
||||||
case len(ws) >= 2 && ws[1] == ':':
|
case len(ws) >= 2 && ws[1] == ':':
|
||||||
return win32.utf16_to_utf8(ws, allocator), nil
|
return win32.utf16_to_utf8(ws, allocator)
|
||||||
case has_prefix(ws, `UNC\`):
|
case has_prefix(ws, `UNC\`):
|
||||||
ws[3] = '\\' // override data in buffer
|
ws[3] = '\\' // override data in buffer
|
||||||
return win32.utf16_to_utf8(ws[3:], allocator), nil
|
return win32.utf16_to_utf8(ws[3:], allocator)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -560,9 +560,9 @@ _normalize_link_path :: proc(p: []u16, allocator: runtime.Allocator) -> (str: st
|
|||||||
ws = ws[4:]
|
ws = ws[4:]
|
||||||
if len(ws) > 3 && has_prefix(ws, `UNC`) {
|
if len(ws) > 3 && has_prefix(ws, `UNC`) {
|
||||||
ws[2] = '\\'
|
ws[2] = '\\'
|
||||||
return win32.utf16_to_utf8(ws[2:], allocator), nil
|
return win32.utf16_to_utf8(ws[2:], allocator)
|
||||||
}
|
}
|
||||||
return win32.utf16_to_utf8(ws, allocator), nil
|
return win32.utf16_to_utf8(ws, allocator)
|
||||||
}
|
}
|
||||||
return "", .Invalid_Path
|
return "", .Invalid_Path
|
||||||
}
|
}
|
||||||
@@ -593,7 +593,7 @@ _read_link :: proc(name: string, allocator: runtime.Allocator) -> (s: string, er
|
|||||||
pb[rb.SubstituteNameOffset+rb.SubstituteNameLength] = 0
|
pb[rb.SubstituteNameOffset+rb.SubstituteNameLength] = 0
|
||||||
p := pb[rb.SubstituteNameOffset:][:rb.SubstituteNameLength]
|
p := pb[rb.SubstituteNameOffset:][:rb.SubstituteNameLength]
|
||||||
if rb.Flags & win32.SYMLINK_FLAG_RELATIVE != 0 {
|
if rb.Flags & win32.SYMLINK_FLAG_RELATIVE != 0 {
|
||||||
return win32.utf16_to_utf8(p, allocator), nil
|
return win32.utf16_to_utf8(p, allocator)
|
||||||
}
|
}
|
||||||
return _normalize_link_path(p, allocator)
|
return _normalize_link_path(p, allocator)
|
||||||
|
|
||||||
|
|||||||
@@ -57,7 +57,7 @@ full_path_from_name :: proc(name: string, allocator: runtime.Allocator) -> (path
|
|||||||
if n == 0 {
|
if n == 0 {
|
||||||
return "", _get_platform_error()
|
return "", _get_platform_error()
|
||||||
}
|
}
|
||||||
return win32.utf16_to_utf8(buf[:n], allocator), nil
|
return win32.utf16_to_utf8(buf[:n], allocator)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -131,7 +131,7 @@ _cleanpath_from_handle :: proc(f: ^File, allocator: runtime.Allocator) -> (strin
|
|||||||
}
|
}
|
||||||
buf := make([]u16, max(n, 260)+1, _temp_allocator())
|
buf := make([]u16, max(n, 260)+1, _temp_allocator())
|
||||||
n = win32.GetFinalPathNameByHandleW(h, raw_data(buf), u32(len(buf)), 0)
|
n = win32.GetFinalPathNameByHandleW(h, raw_data(buf), u32(len(buf)), 0)
|
||||||
return _cleanpath_from_buf(buf[:n], allocator), nil
|
return _cleanpath_from_buf(buf[:n], allocator)
|
||||||
}
|
}
|
||||||
|
|
||||||
_cleanpath_from_handle_u16 :: proc(f: ^File) -> ([]u16, Error) {
|
_cleanpath_from_handle_u16 :: proc(f: ^File) -> ([]u16, Error) {
|
||||||
@@ -149,7 +149,7 @@ _cleanpath_from_handle_u16 :: proc(f: ^File) -> ([]u16, Error) {
|
|||||||
return _cleanpath_strip_prefix(buf[:n]), nil
|
return _cleanpath_strip_prefix(buf[:n]), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
_cleanpath_from_buf :: proc(buf: []u16, allocator: runtime.Allocator) -> string {
|
_cleanpath_from_buf :: proc(buf: []u16, allocator: runtime.Allocator) -> (string, runtime.Allocator_Error) {
|
||||||
buf := buf
|
buf := buf
|
||||||
buf = _cleanpath_strip_prefix(buf)
|
buf = _cleanpath_strip_prefix(buf)
|
||||||
return win32.utf16_to_utf8(buf, allocator)
|
return win32.utf16_to_utf8(buf, allocator)
|
||||||
@@ -194,15 +194,15 @@ file_type_mode :: proc(h: win32.HANDLE) -> File_Mode {
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
_file_mode_from_file_attributes :: proc(FileAttributes: win32.DWORD, h: win32.HANDLE, ReparseTag: win32.DWORD) -> (mode: File_Mode) {
|
_file_mode_from_file_attributes :: proc(file_attributes: win32.DWORD, h: win32.HANDLE, ReparseTag: win32.DWORD) -> (mode: File_Mode) {
|
||||||
if FileAttributes & win32.FILE_ATTRIBUTE_READONLY != 0 {
|
if file_attributes & win32.FILE_ATTRIBUTE_READONLY != 0 {
|
||||||
mode |= 0o444
|
mode |= 0o444
|
||||||
} else {
|
} else {
|
||||||
mode |= 0o666
|
mode |= 0o666
|
||||||
}
|
}
|
||||||
|
|
||||||
is_sym := false
|
is_sym := false
|
||||||
if FileAttributes & win32.FILE_ATTRIBUTE_REPARSE_POINT == 0 {
|
if file_attributes & win32.FILE_ATTRIBUTE_REPARSE_POINT == 0 {
|
||||||
is_sym = false
|
is_sym = false
|
||||||
} else {
|
} else {
|
||||||
is_sym = ReparseTag == win32.IO_REPARSE_TAG_SYMLINK || ReparseTag == win32.IO_REPARSE_TAG_MOUNT_POINT
|
is_sym = ReparseTag == win32.IO_REPARSE_TAG_SYMLINK || ReparseTag == win32.IO_REPARSE_TAG_MOUNT_POINT
|
||||||
@@ -211,7 +211,7 @@ _file_mode_from_file_attributes :: proc(FileAttributes: win32.DWORD, h: win32.HA
|
|||||||
if is_sym {
|
if is_sym {
|
||||||
mode |= File_Mode_Sym_Link
|
mode |= File_Mode_Sym_Link
|
||||||
} else {
|
} else {
|
||||||
if FileAttributes & win32.FILE_ATTRIBUTE_DIRECTORY != 0 {
|
if file_attributes & win32.FILE_ATTRIBUTE_DIRECTORY != 0 {
|
||||||
mode |= 0o111 | File_Mode_Dir
|
mode |= 0o111 | File_Mode_Dir
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -10,6 +10,6 @@ mkdir_temp :: proc(dir, pattern: string, allocator: runtime.Allocator) -> (strin
|
|||||||
return _mkdir_temp(dir, pattern, allocator)
|
return _mkdir_temp(dir, pattern, allocator)
|
||||||
}
|
}
|
||||||
|
|
||||||
temp_dir :: proc(allocator: runtime.Allocator) -> string {
|
temp_dir :: proc(allocator: runtime.Allocator) -> (string, Error) {
|
||||||
return _temp_dir(allocator)
|
return _temp_dir(allocator)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,10 +12,10 @@ _mkdir_temp :: proc(dir, pattern: string, allocator: runtime.Allocator) -> (stri
|
|||||||
return "", nil
|
return "", nil
|
||||||
}
|
}
|
||||||
|
|
||||||
_temp_dir :: proc(allocator: runtime.Allocator) -> string {
|
_temp_dir :: proc(allocator: runtime.Allocator) -> (string, runtime.Allocator_Error) {
|
||||||
n := win32.GetTempPathW(0, nil)
|
n := win32.GetTempPathW(0, nil)
|
||||||
if n == 0 {
|
if n == 0 {
|
||||||
return ""
|
return "", nil
|
||||||
}
|
}
|
||||||
b := make([]u16, max(win32.MAX_PATH, n), _temp_allocator())
|
b := make([]u16, max(win32.MAX_PATH, n), _temp_allocator())
|
||||||
n = win32.GetTempPathW(u32(len(b)), raw_data(b))
|
n = win32.GetTempPathW(u32(len(b)), raw_data(b))
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ full_path_from_name :: proc(name: string, allocator := context.allocator) -> (pa
|
|||||||
return "", Errno(win32.GetLastError())
|
return "", Errno(win32.GetLastError())
|
||||||
}
|
}
|
||||||
if n <= u32(len(buf)) {
|
if n <= u32(len(buf)) {
|
||||||
return win32.utf16_to_utf8(buf[:n], allocator), ERROR_NONE
|
return win32.utf16_to_utf8(buf[:n], allocator) or_else "", ERROR_NONE
|
||||||
}
|
}
|
||||||
resize(&buf, len(buf)*2)
|
resize(&buf, len(buf)*2)
|
||||||
}
|
}
|
||||||
@@ -136,7 +136,7 @@ cleanpath_from_handle :: proc(fd: Handle) -> (string, Errno) {
|
|||||||
if err != 0 {
|
if err != 0 {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
return win32.utf16_to_utf8(buf, context.allocator), err
|
return win32.utf16_to_utf8(buf, context.allocator) or_else "", err
|
||||||
}
|
}
|
||||||
@(private)
|
@(private)
|
||||||
cleanpath_from_handle_u16 :: proc(fd: Handle) -> ([]u16, Errno) {
|
cleanpath_from_handle_u16 :: proc(fd: Handle) -> ([]u16, Errno) {
|
||||||
@@ -157,7 +157,7 @@ cleanpath_from_handle_u16 :: proc(fd: Handle) -> ([]u16, Errno) {
|
|||||||
cleanpath_from_buf :: proc(buf: []u16) -> string {
|
cleanpath_from_buf :: proc(buf: []u16) -> string {
|
||||||
buf := buf
|
buf := buf
|
||||||
buf = cleanpath_strip_prefix(buf)
|
buf = cleanpath_strip_prefix(buf)
|
||||||
return win32.utf16_to_utf8(buf, context.allocator)
|
return win32.utf16_to_utf8(buf, context.allocator) or_else ""
|
||||||
}
|
}
|
||||||
|
|
||||||
@(private)
|
@(private)
|
||||||
|
|||||||
@@ -126,7 +126,7 @@ _open_file_dialog :: proc(title: string, dir: string,
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
file_name := utf16_to_utf8(file_buf[:], allocator)
|
file_name, _ := utf16_to_utf8(file_buf[:], allocator)
|
||||||
path = strings.trim_right_null(file_name)
|
path = strings.trim_right_null(file_name)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,6 +10,6 @@ foreign {
|
|||||||
get_cwd :: proc(allocator := context.temp_allocator) -> string {
|
get_cwd :: proc(allocator := context.temp_allocator) -> string {
|
||||||
buffer := make([]u16, MAX_PATH_WIDE, allocator)
|
buffer := make([]u16, MAX_PATH_WIDE, allocator)
|
||||||
_get_cwd_wide(Wstring(&buffer[0]), MAX_PATH_WIDE)
|
_get_cwd_wide(Wstring(&buffer[0]), MAX_PATH_WIDE)
|
||||||
file := utf16_to_utf8(buffer[:], allocator)
|
file, _ := utf16_to_utf8(buffer[:], allocator)
|
||||||
return strings.trim_right_null(file)
|
return strings.trim_right_null(file)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
// +build windows
|
// +build windows
|
||||||
package win32
|
package win32
|
||||||
|
|
||||||
|
import "core:runtime"
|
||||||
|
|
||||||
Uint_Ptr :: distinct uintptr
|
Uint_Ptr :: distinct uintptr
|
||||||
Int_Ptr :: distinct int
|
Int_Ptr :: distinct int
|
||||||
Long_Ptr :: distinct int
|
Long_Ptr :: distinct int
|
||||||
@@ -858,14 +860,14 @@ utf8_to_wstring :: proc(s: string, allocator := context.temp_allocator) -> Wstri
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
wstring_to_utf8 :: proc(s: Wstring, N: int, allocator := context.temp_allocator) -> string {
|
wstring_to_utf8 :: proc(s: Wstring, N: int, allocator := context.temp_allocator) -> (str: string, err: runtime.Allocator_Error) {
|
||||||
if N == 0 {
|
if N == 0 {
|
||||||
return ""
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
n := wide_char_to_multi_byte(CP_UTF8, WC_ERR_INVALID_CHARS, s, i32(N), nil, 0, nil, nil)
|
n := wide_char_to_multi_byte(CP_UTF8, WC_ERR_INVALID_CHARS, s, i32(N), nil, 0, nil, nil)
|
||||||
if n == 0 {
|
if n == 0 {
|
||||||
return ""
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// If N == -1 the call to wide_char_to_multi_byte assume the wide string is null terminated
|
// If N == -1 the call to wide_char_to_multi_byte assume the wide string is null terminated
|
||||||
@@ -873,11 +875,11 @@ wstring_to_utf8 :: proc(s: Wstring, N: int, allocator := context.temp_allocator)
|
|||||||
// also null terminated.
|
// also null terminated.
|
||||||
// If N != -1 it assumes the wide string is not null terminated and the resulting string
|
// If N != -1 it assumes the wide string is not null terminated and the resulting string
|
||||||
// will not be null terminated, we therefore have to force it to be null terminated manually.
|
// will not be null terminated, we therefore have to force it to be null terminated manually.
|
||||||
text := make([]byte, n+1 if N != -1 else n, allocator)
|
text := make([]byte, n+1 if N != -1 else n, allocator) or_return
|
||||||
|
|
||||||
if n1 := wide_char_to_multi_byte(CP_UTF8, WC_ERR_INVALID_CHARS, s, i32(N), cstring(&text[0]), n, nil, nil); n1 == 0 {
|
if n1 := wide_char_to_multi_byte(CP_UTF8, WC_ERR_INVALID_CHARS, s, i32(N), cstring(&text[0]), n, nil, nil); n1 == 0 {
|
||||||
delete(text, allocator)
|
delete(text, allocator)
|
||||||
return ""
|
return "", nil
|
||||||
}
|
}
|
||||||
|
|
||||||
for i in 0..<n {
|
for i in 0..<n {
|
||||||
@@ -887,12 +889,12 @@ wstring_to_utf8 :: proc(s: Wstring, N: int, allocator := context.temp_allocator)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return string(text[:n])
|
return string(text[:n]), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
utf16_to_utf8 :: proc(s: []u16, allocator := context.temp_allocator) -> string {
|
utf16_to_utf8 :: proc(s: []u16, allocator := context.temp_allocator) -> (string, runtime.Allocator_Error) {
|
||||||
if len(s) == 0 {
|
if len(s) == 0 {
|
||||||
return ""
|
return "", nil
|
||||||
}
|
}
|
||||||
return wstring_to_utf8(cast(Wstring)&s[0], len(s), allocator)
|
return wstring_to_utf8(cast(Wstring)&s[0], len(s), allocator)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,41 +1,41 @@
|
|||||||
package win32_tests
|
package win32_tests
|
||||||
|
|
||||||
import "core:sys/win32"
|
import win32 "core:sys/windows"
|
||||||
import "core:testing"
|
import "core:testing"
|
||||||
|
|
||||||
utf16_to_utf8 :: proc(t: ^testing.T, str: []u16, comparison: string, expected_result: bool, loc := #caller_location) {
|
utf16_to_utf8 :: proc(t: ^testing.T, str: []u16, comparison: string, expected_result: bool, loc := #caller_location) {
|
||||||
result := win32.utf16_to_utf8(str[:]);
|
result, _ := win32.utf16_to_utf8(str[:])
|
||||||
testing.expect(t, (result == comparison) == expected_result, "Incorrect utf16_to_utf8 conversion", loc);
|
testing.expect(t, (result == comparison) == expected_result, "Incorrect utf16_to_utf8 conversion", loc)
|
||||||
}
|
}
|
||||||
|
|
||||||
wstring_to_utf8 :: proc(t: ^testing.T, str: []u16, comparison: string, expected_result: bool, loc := #caller_location) {
|
wstring_to_utf8 :: proc(t: ^testing.T, str: []u16, comparison: string, expected_result: bool, loc := #caller_location) {
|
||||||
result := win32.wstring_to_utf8(nil if len(str) == 0 else cast(win32.Wstring)&str[0], -1);
|
result, _ := win32.wstring_to_utf8(nil if len(str) == 0 else cast(win32.Wstring)&str[0], -1)
|
||||||
testing.expect(t, (result == comparison) == expected_result, "Incorrect wstring_to_utf8 conversion", loc);
|
testing.expect(t, (result == comparison) == expected_result, "Incorrect wstring_to_utf8 conversion", loc)
|
||||||
}
|
}
|
||||||
|
|
||||||
@test
|
@test
|
||||||
test_utf :: proc(t: ^testing.T) {
|
test_utf :: proc(t: ^testing.T) {
|
||||||
utf16_to_utf8(t, []u16{}, "", true);
|
utf16_to_utf8(t, []u16{}, "", true)
|
||||||
utf16_to_utf8(t, []u16{0}, "", true);
|
utf16_to_utf8(t, []u16{0}, "", true)
|
||||||
utf16_to_utf8(t, []u16{0, 't', 'e', 's', 't'}, "", true);
|
utf16_to_utf8(t, []u16{0, 't', 'e', 's', 't'}, "", true)
|
||||||
utf16_to_utf8(t, []u16{0, 't', 'e', 's', 't', 0}, "", true);
|
utf16_to_utf8(t, []u16{0, 't', 'e', 's', 't', 0}, "", true)
|
||||||
utf16_to_utf8(t, []u16{'t', 'e', 's', 't'}, "test", true);
|
utf16_to_utf8(t, []u16{'t', 'e', 's', 't'}, "test", true)
|
||||||
utf16_to_utf8(t, []u16{'t', 'e', 's', 't', 0}, "test", true);
|
utf16_to_utf8(t, []u16{'t', 'e', 's', 't', 0}, "test", true)
|
||||||
utf16_to_utf8(t, []u16{'t', 'e', 0, 's', 't'}, "te", true);
|
utf16_to_utf8(t, []u16{'t', 'e', 0, 's', 't'}, "te", true)
|
||||||
utf16_to_utf8(t, []u16{'t', 'e', 0, 's', 't', 0}, "te", true);
|
utf16_to_utf8(t, []u16{'t', 'e', 0, 's', 't', 0}, "te", true)
|
||||||
|
|
||||||
wstring_to_utf8(t, []u16{}, "", true);
|
wstring_to_utf8(t, []u16{}, "", true)
|
||||||
wstring_to_utf8(t, []u16{0}, "", true);
|
wstring_to_utf8(t, []u16{0}, "", true)
|
||||||
wstring_to_utf8(t, []u16{0, 't', 'e', 's', 't'}, "", true);
|
wstring_to_utf8(t, []u16{0, 't', 'e', 's', 't'}, "", true)
|
||||||
wstring_to_utf8(t, []u16{0, 't', 'e', 's', 't', 0}, "", true);
|
wstring_to_utf8(t, []u16{0, 't', 'e', 's', 't', 0}, "", true)
|
||||||
wstring_to_utf8(t, []u16{'t', 'e', 's', 't', 0}, "test", true);
|
wstring_to_utf8(t, []u16{'t', 'e', 's', 't', 0}, "test", true)
|
||||||
wstring_to_utf8(t, []u16{'t', 'e', 0, 's', 't'}, "te", true);
|
wstring_to_utf8(t, []u16{'t', 'e', 0, 's', 't'}, "te", true)
|
||||||
wstring_to_utf8(t, []u16{'t', 'e', 0, 's', 't', 0}, "te", true);
|
wstring_to_utf8(t, []u16{'t', 'e', 0, 's', 't', 0}, "te", true)
|
||||||
|
|
||||||
// WARNING: Passing a non-zero-terminated string to wstring_to_utf8 is dangerous,
|
// WARNING: Passing a non-zero-terminated string to wstring_to_utf8 is dangerous,
|
||||||
// as it will go out of bounds looking for a zero.
|
// as it will go out of bounds looking for a zero.
|
||||||
// It will "fail" or "succeed" by having a zero just after the end of the input string or not.
|
// It will "fail" or "succeed" by having a zero just after the end of the input string or not.
|
||||||
wstring_to_utf8(t, []u16{'t', 'e', 's', 't'}, "test", false);
|
wstring_to_utf8(t, []u16{'t', 'e', 's', 't'}, "test", false)
|
||||||
wstring_to_utf8(t, []u16{'t', 'e', 's', 't', 0}[:4], "test", true);
|
wstring_to_utf8(t, []u16{'t', 'e', 's', 't', 0}[:4], "test", true)
|
||||||
wstring_to_utf8(t, []u16{'t', 'e', 's', 't', 'q'}[:4], "test", false);
|
wstring_to_utf8(t, []u16{'t', 'e', 's', 't', 'q'}[:4], "test", false)
|
||||||
}
|
}
|
||||||
@@ -272,6 +272,11 @@ foreign kernel32 {
|
|||||||
HeapReAlloc :: proc(hHeap: HANDLE, dwFlags: DWORD, lpMem: LPVOID, dwBytes: SIZE_T) -> LPVOID ---
|
HeapReAlloc :: proc(hHeap: HANDLE, dwFlags: DWORD, lpMem: LPVOID, dwBytes: SIZE_T) -> LPVOID ---
|
||||||
HeapFree :: proc(hHeap: HANDLE, dwFlags: DWORD, lpMem: LPVOID) -> BOOL ---
|
HeapFree :: proc(hHeap: HANDLE, dwFlags: DWORD, lpMem: LPVOID) -> BOOL ---
|
||||||
|
|
||||||
|
LocalAlloc :: proc(flags: UINT, bytes: SIZE_T) -> LPVOID ---
|
||||||
|
LocalReAlloc :: proc(mem: LPVOID, bytes: SIZE_T, flags: UINT) -> LPVOID ---
|
||||||
|
LocalFree :: proc(mem: LPVOID) -> LPVOID ---
|
||||||
|
|
||||||
|
|
||||||
ReadDirectoryChangesW :: proc(
|
ReadDirectoryChangesW :: proc(
|
||||||
hDirectory: HANDLE,
|
hDirectory: HANDLE,
|
||||||
lpBuffer: LPVOID,
|
lpBuffer: LPVOID,
|
||||||
|
|||||||
+13
-13
@@ -2,7 +2,7 @@
|
|||||||
package sys_windows
|
package sys_windows
|
||||||
|
|
||||||
import "core:strings"
|
import "core:strings"
|
||||||
import "core:sys/win32"
|
import "core:runtime"
|
||||||
import "core:intrinsics"
|
import "core:intrinsics"
|
||||||
|
|
||||||
L :: intrinsics.constant_utf16_cstring
|
L :: intrinsics.constant_utf16_cstring
|
||||||
@@ -56,16 +56,16 @@ utf8_to_wstring :: proc(s: string, allocator := context.temp_allocator) -> wstri
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
wstring_to_utf8 :: proc(s: wstring, N: int, allocator := context.temp_allocator) -> (res: string) {
|
wstring_to_utf8 :: proc(s: wstring, N: int, allocator := context.temp_allocator) -> (res: string, err: runtime.Allocator_Error) {
|
||||||
context.allocator = allocator
|
context.allocator = allocator
|
||||||
|
|
||||||
if N <= 0 {
|
if N <= 0 {
|
||||||
return ""
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
n := WideCharToMultiByte(CP_UTF8, WC_ERR_INVALID_CHARS, s, i32(N), nil, 0, nil, nil)
|
n := WideCharToMultiByte(CP_UTF8, WC_ERR_INVALID_CHARS, s, i32(N), nil, 0, nil, nil)
|
||||||
if n == 0 {
|
if n == 0 {
|
||||||
return ""
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// If N == -1 the call to WideCharToMultiByte assume the wide string is null terminated
|
// If N == -1 the call to WideCharToMultiByte assume the wide string is null terminated
|
||||||
@@ -73,12 +73,12 @@ wstring_to_utf8 :: proc(s: wstring, N: int, allocator := context.temp_allocator)
|
|||||||
// also null terminated.
|
// also null terminated.
|
||||||
// If N != -1 it assumes the wide string is not null terminated and the resulting string
|
// If N != -1 it assumes the wide string is not null terminated and the resulting string
|
||||||
// will not be null terminated, we therefore have to force it to be null terminated manually.
|
// will not be null terminated, we therefore have to force it to be null terminated manually.
|
||||||
text := make([]byte, n+1 if N != -1 else n)
|
text := make([]byte, n+1 if N != -1 else n) or_return
|
||||||
|
|
||||||
n1 := WideCharToMultiByte(CP_UTF8, WC_ERR_INVALID_CHARS, s, i32(N), raw_data(text), n, nil, nil)
|
n1 := WideCharToMultiByte(CP_UTF8, WC_ERR_INVALID_CHARS, s, i32(N), raw_data(text), n, nil, nil)
|
||||||
if n1 == 0 {
|
if n1 == 0 {
|
||||||
delete(text, allocator)
|
delete(text, allocator)
|
||||||
return ""
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
for i in 0..<n {
|
for i in 0..<n {
|
||||||
@@ -87,12 +87,12 @@ wstring_to_utf8 :: proc(s: wstring, N: int, allocator := context.temp_allocator)
|
|||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return string(text[:n])
|
return string(text[:n]), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
utf16_to_utf8 :: proc(s: []u16, allocator := context.temp_allocator) -> string {
|
utf16_to_utf8 :: proc(s: []u16, allocator := context.temp_allocator) -> (res: string, err: runtime.Allocator_Error) {
|
||||||
if len(s) == 0 {
|
if len(s) == 0 {
|
||||||
return ""
|
return "", nil
|
||||||
}
|
}
|
||||||
return wstring_to_utf8(raw_data(s), len(s), allocator)
|
return wstring_to_utf8(raw_data(s), len(s), allocator)
|
||||||
}
|
}
|
||||||
@@ -216,7 +216,7 @@ get_computer_name_and_account_sid :: proc(username: string) -> (computer_name: s
|
|||||||
if !res {
|
if !res {
|
||||||
return "", {}, false
|
return "", {}, false
|
||||||
}
|
}
|
||||||
computer_name = utf16_to_utf8(cname_w, context.temp_allocator)
|
computer_name = utf16_to_utf8(cname_w, context.temp_allocator) or_else ""
|
||||||
|
|
||||||
ok = true
|
ok = true
|
||||||
return
|
return
|
||||||
@@ -306,7 +306,7 @@ add_user_profile :: proc(username: string) -> (ok: bool, profile_path: string) {
|
|||||||
if res == false {
|
if res == false {
|
||||||
return false, ""
|
return false, ""
|
||||||
}
|
}
|
||||||
defer win32.local_free(sb)
|
defer LocalFree(sb)
|
||||||
|
|
||||||
pszProfilePath := make([]u16, 257, context.temp_allocator)
|
pszProfilePath := make([]u16, 257, context.temp_allocator)
|
||||||
res2 := CreateProfile(
|
res2 := CreateProfile(
|
||||||
@@ -318,7 +318,7 @@ add_user_profile :: proc(username: string) -> (ok: bool, profile_path: string) {
|
|||||||
if res2 != 0 {
|
if res2 != 0 {
|
||||||
return false, ""
|
return false, ""
|
||||||
}
|
}
|
||||||
profile_path = wstring_to_utf8(&pszProfilePath[0], 257)
|
profile_path = wstring_to_utf8(&pszProfilePath[0], 257) or_else ""
|
||||||
|
|
||||||
return true, profile_path
|
return true, profile_path
|
||||||
}
|
}
|
||||||
@@ -336,7 +336,7 @@ delete_user_profile :: proc(username: string) -> (ok: bool) {
|
|||||||
if res == false {
|
if res == false {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
defer win32.local_free(sb)
|
defer LocalFree(sb)
|
||||||
|
|
||||||
res2 := DeleteProfileW(
|
res2 := DeleteProfileW(
|
||||||
sb,
|
sb,
|
||||||
|
|||||||
Reference in New Issue
Block a user