mirror of
https://github.com/odin-lang/Odin.git
synced 2026-05-27 22:25:09 +00:00
Merge branch 'master' into os2-process-linux
This commit is contained in:
@@ -334,6 +334,27 @@ panicf :: proc(fmt: string, args: ..any, loc := #caller_location) -> ! {
|
||||
message := tprintf(fmt, ..args)
|
||||
p("Panic", message, loc)
|
||||
}
|
||||
|
||||
// Creates a formatted C string
|
||||
//
|
||||
// *Allocates Using Context's Allocator*
|
||||
//
|
||||
// Inputs:
|
||||
// - args: A variadic list of arguments to be formatted.
|
||||
// - sep: An optional separator string (default is a single space).
|
||||
//
|
||||
// Returns: A formatted C string.
|
||||
//
|
||||
@(require_results)
|
||||
caprint :: proc(args: ..any, sep := " ", allocator := context.allocator) -> cstring {
|
||||
str: strings.Builder
|
||||
strings.builder_init(&str, allocator)
|
||||
sbprint(&str, ..args, sep=sep)
|
||||
strings.write_byte(&str, 0)
|
||||
s := strings.to_string(str)
|
||||
return cstring(raw_data(s))
|
||||
}
|
||||
|
||||
// Creates a formatted C string
|
||||
//
|
||||
// *Allocates Using Context's Allocator*
|
||||
@@ -346,9 +367,9 @@ panicf :: proc(fmt: string, args: ..any, loc := #caller_location) -> ! {
|
||||
// Returns: A formatted C string
|
||||
//
|
||||
@(require_results)
|
||||
caprintf :: proc(format: string, args: ..any, newline := false) -> cstring {
|
||||
caprintf :: proc(format: string, args: ..any, allocator := context.allocator, newline := false) -> cstring {
|
||||
str: strings.Builder
|
||||
strings.builder_init(&str)
|
||||
strings.builder_init(&str, allocator)
|
||||
sbprintf(&str, format, ..args, newline=newline)
|
||||
strings.write_byte(&str, 0)
|
||||
s := strings.to_string(str)
|
||||
@@ -365,8 +386,8 @@ caprintf :: proc(format: string, args: ..any, newline := false) -> cstring {
|
||||
// Returns: A formatted C string
|
||||
//
|
||||
@(require_results)
|
||||
caprintfln :: proc(format: string, args: ..any) -> cstring {
|
||||
return caprintf(format, ..args, newline=true)
|
||||
caprintfln :: proc(format: string, args: ..any, allocator := context.allocator) -> cstring {
|
||||
return caprintf(format, ..args, allocator=allocator, newline=true)
|
||||
}
|
||||
// Creates a formatted C string
|
||||
//
|
||||
@@ -380,12 +401,7 @@ caprintfln :: proc(format: string, args: ..any) -> cstring {
|
||||
//
|
||||
@(require_results)
|
||||
ctprint :: proc(args: ..any, sep := " ") -> cstring {
|
||||
str: strings.Builder
|
||||
strings.builder_init(&str, context.temp_allocator)
|
||||
sbprint(&str, ..args, sep=sep)
|
||||
strings.write_byte(&str, 0)
|
||||
s := strings.to_string(str)
|
||||
return cstring(raw_data(s))
|
||||
return caprint(args=args, sep=sep, allocator=context.temp_allocator)
|
||||
}
|
||||
// Creates a formatted C string
|
||||
//
|
||||
@@ -400,12 +416,7 @@ ctprint :: proc(args: ..any, sep := " ") -> cstring {
|
||||
//
|
||||
@(require_results)
|
||||
ctprintf :: proc(format: string, args: ..any, newline := false) -> cstring {
|
||||
str: strings.Builder
|
||||
strings.builder_init(&str, context.temp_allocator)
|
||||
sbprintf(&str, format, ..args, newline=newline)
|
||||
strings.write_byte(&str, 0)
|
||||
s := strings.to_string(str)
|
||||
return cstring(raw_data(s))
|
||||
return caprintf(format=format, args=args, allocator=context.temp_allocator, newline=newline)
|
||||
}
|
||||
// Creates a formatted C string, followed by a newline.
|
||||
//
|
||||
@@ -419,7 +430,7 @@ ctprintf :: proc(format: string, args: ..any, newline := false) -> cstring {
|
||||
//
|
||||
@(require_results)
|
||||
ctprintfln :: proc(format: string, args: ..any) -> cstring {
|
||||
return ctprintf(format, ..args, newline=true)
|
||||
return caprintf(format=format, args=args, allocator=context.temp_allocator, newline=true)
|
||||
}
|
||||
// Formats using the default print settings and writes to the given strings.Builder
|
||||
//
|
||||
|
||||
@@ -1,21 +1,80 @@
|
||||
package os2
|
||||
|
||||
import "base:runtime"
|
||||
import "core:slice"
|
||||
|
||||
read_directory :: proc(f: ^File, n: int, allocator: runtime.Allocator) -> (fi: []File_Info, err: Error) {
|
||||
return _read_directory(f, n, allocator)
|
||||
@(require_results)
|
||||
read_directory :: proc(f: ^File, n: int, allocator: runtime.Allocator) -> (files: []File_Info, err: Error) {
|
||||
if f == nil {
|
||||
return nil, .Invalid_File
|
||||
}
|
||||
|
||||
n := n
|
||||
size := n
|
||||
if n <= 0 {
|
||||
n = -1
|
||||
size = 100
|
||||
}
|
||||
|
||||
TEMP_ALLOCATOR_GUARD()
|
||||
|
||||
it := read_directory_iterator_create(f) or_return
|
||||
defer _read_directory_iterator_destroy(&it)
|
||||
|
||||
dfi := make([dynamic]File_Info, 0, size, temp_allocator())
|
||||
defer if err != nil {
|
||||
for fi in dfi {
|
||||
file_info_delete(fi, allocator)
|
||||
}
|
||||
}
|
||||
|
||||
for fi, index in read_directory_iterator(&it) {
|
||||
if n > 0 && index == n {
|
||||
break
|
||||
}
|
||||
append(&dfi, file_info_clone(fi, allocator) or_return)
|
||||
}
|
||||
|
||||
return slice.clone(dfi[:], allocator)
|
||||
}
|
||||
|
||||
|
||||
@(require_results)
|
||||
read_all_directory :: proc(f: ^File, allocator: runtime.Allocator) -> (fi: []File_Info, err: Error) {
|
||||
return read_directory(f, -1, allocator)
|
||||
}
|
||||
|
||||
@(require_results)
|
||||
read_directory_by_path :: proc(path: string, n: int, allocator: runtime.Allocator) -> (fi: []File_Info, err: Error) {
|
||||
f := open(path) or_return
|
||||
defer close(f)
|
||||
return read_directory(f, n, allocator)
|
||||
}
|
||||
|
||||
@(require_results)
|
||||
read_all_directory_by_path :: proc(path: string, allocator: runtime.Allocator) -> (fi: []File_Info, err: Error) {
|
||||
return read_directory_by_path(path, -1, allocator)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Read_Directory_Iterator :: struct {
|
||||
f: ^File,
|
||||
impl: Read_Directory_Iterator_Impl,
|
||||
}
|
||||
|
||||
|
||||
@(require_results)
|
||||
read_directory_iterator_create :: proc(f: ^File) -> (Read_Directory_Iterator, Error) {
|
||||
return _read_directory_iterator_create(f)
|
||||
}
|
||||
|
||||
read_directory_iterator_destroy :: proc(it: ^Read_Directory_Iterator) {
|
||||
_read_directory_iterator_destroy(it)
|
||||
}
|
||||
|
||||
|
||||
// NOTE(bill): `File_Info` does not need to deleted on each iteration. Any copies must be manually copied with `file_info_clone`
|
||||
@(require_results)
|
||||
read_directory_iterator :: proc(it: ^Read_Directory_Iterator) -> (fi: File_Info, index: int, ok: bool) {
|
||||
return _read_directory_iterator(it)
|
||||
}
|
||||
|
||||
@@ -1,8 +1,20 @@
|
||||
//+private
|
||||
package os2
|
||||
|
||||
import "base:runtime"
|
||||
Read_Directory_Iterator_Impl :: struct {
|
||||
|
||||
@(private)
|
||||
_read_directory :: proc(f: ^File, n: int, allocator: runtime.Allocator) -> (files: []File_Info, err: Error) {
|
||||
}
|
||||
|
||||
|
||||
@(require_results)
|
||||
_read_directory_iterator :: proc(it: ^Read_Directory_Iterator) -> (fi: File_Info, index: int, ok: bool) {
|
||||
return
|
||||
}
|
||||
|
||||
@(require_results)
|
||||
_read_directory_iterator_create :: proc(f: ^File) -> (Read_Directory_Iterator, Error) {
|
||||
return {}, nil
|
||||
}
|
||||
|
||||
_read_directory_iterator_destroy :: proc(it: ^Read_Directory_Iterator) {
|
||||
}
|
||||
|
||||
@@ -1,67 +1,92 @@
|
||||
//+private
|
||||
package os2
|
||||
|
||||
import "base:runtime"
|
||||
import "core:time"
|
||||
import win32 "core:sys/windows"
|
||||
|
||||
@(private)
|
||||
_read_directory :: proc(f: ^File, n: int, allocator: runtime.Allocator) -> (files: []File_Info, err: Error) {
|
||||
find_data_to_file_info :: proc(base_path: string, d: ^win32.WIN32_FIND_DATAW, allocator: runtime.Allocator) -> (fi: File_Info, err: Error) {
|
||||
// Ignore "." and ".."
|
||||
if d.cFileName[0] == '.' && d.cFileName[1] == 0 {
|
||||
return
|
||||
}
|
||||
if d.cFileName[0] == '.' && d.cFileName[1] == '.' && d.cFileName[2] == 0 {
|
||||
return
|
||||
}
|
||||
path := concatenate({base_path, `\`, win32_utf16_to_utf8(d.cFileName[:], temp_allocator()) or_else ""}, allocator) or_return
|
||||
fi.fullpath = path
|
||||
fi.name = basename(path)
|
||||
fi.size = i64(d.nFileSizeHigh)<<32 + i64(d.nFileSizeLow)
|
||||
@(private="file")
|
||||
find_data_to_file_info :: proc(base_path: string, d: ^win32.WIN32_FIND_DATAW, allocator: runtime.Allocator) -> (fi: File_Info, err: Error) {
|
||||
// Ignore "." and ".."
|
||||
if d.cFileName[0] == '.' && d.cFileName[1] == 0 {
|
||||
return
|
||||
}
|
||||
if d.cFileName[0] == '.' && d.cFileName[1] == '.' && d.cFileName[2] == 0 {
|
||||
return
|
||||
}
|
||||
path := concatenate({base_path, `\`, win32_utf16_to_utf8(d.cFileName[:], temp_allocator()) or_else ""}, allocator) or_return
|
||||
|
||||
if d.dwFileAttributes & win32.FILE_ATTRIBUTE_READONLY != 0 {
|
||||
fi.mode |= 0o444
|
||||
} else {
|
||||
fi.mode |= 0o666
|
||||
}
|
||||
|
||||
is_sym := false
|
||||
if d.dwFileAttributes & win32.FILE_ATTRIBUTE_REPARSE_Point == 0 {
|
||||
is_sym = false
|
||||
} else {
|
||||
is_sym = d.dwReserved0 == win32.IO_REPARSE_TAG_SYMLINK || d.dwReserved0 == win32.IO_REPARSE_TAG_MOUNT_POINT
|
||||
}
|
||||
fi.fullpath = path
|
||||
fi.name = basename(path)
|
||||
fi.size = i64(d.nFileSizeHigh)<<32 + i64(d.nFileSizeLow)
|
||||
|
||||
if is_sym {
|
||||
fi.type = .Symlink
|
||||
} else if d.dwFileAttributes & win32.FILE_ATTRIBUTE_DIRECTORY != 0 {
|
||||
fi.type = .Directory
|
||||
fi.mode |= 0o111
|
||||
}
|
||||
fi.type, fi.mode = _file_type_mode_from_file_attributes(d.dwFileAttributes, nil, d.dwReserved0)
|
||||
|
||||
fi.creation_time = time.unix(0, win32.FILETIME_as_unix_nanoseconds(d.ftCreationTime))
|
||||
fi.modification_time = time.unix(0, win32.FILETIME_as_unix_nanoseconds(d.ftLastWriteTime))
|
||||
fi.access_time = time.unix(0, win32.FILETIME_as_unix_nanoseconds(d.ftLastAccessTime))
|
||||
// fi.inode = u128(u64(d.nFileIndexHigh)<<32 + u64(d.nFileIndexLow))
|
||||
fi.creation_time = time.unix(0, win32.FILETIME_as_unix_nanoseconds(d.ftCreationTime))
|
||||
fi.modification_time = time.unix(0, win32.FILETIME_as_unix_nanoseconds(d.ftLastWriteTime))
|
||||
fi.access_time = time.unix(0, win32.FILETIME_as_unix_nanoseconds(d.ftLastAccessTime))
|
||||
return
|
||||
}
|
||||
|
||||
Read_Directory_Iterator_Impl :: struct {
|
||||
find_data: win32.WIN32_FIND_DATAW,
|
||||
find_handle: win32.HANDLE,
|
||||
path: string,
|
||||
prev_fi: File_Info,
|
||||
no_more_files: bool,
|
||||
index: int,
|
||||
}
|
||||
|
||||
|
||||
@(require_results)
|
||||
_read_directory_iterator :: proc(it: ^Read_Directory_Iterator) -> (fi: File_Info, index: int, ok: bool) {
|
||||
if it.f == nil {
|
||||
return
|
||||
}
|
||||
|
||||
if f == nil {
|
||||
return nil, .Invalid_File
|
||||
for !it.impl.no_more_files {
|
||||
err: Error
|
||||
file_info_delete(it.impl.prev_fi, file_allocator())
|
||||
it.impl.prev_fi = {}
|
||||
|
||||
fi, err = find_data_to_file_info(it.impl.path, &it.impl.find_data, file_allocator())
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
if fi.name != "" {
|
||||
it.impl.prev_fi = fi
|
||||
ok = true
|
||||
index = it.impl.index
|
||||
it.impl.index += 1
|
||||
}
|
||||
|
||||
if !win32.FindNextFileW(it.impl.find_handle, &it.impl.find_data) {
|
||||
e := _get_platform_error()
|
||||
if pe, _ := is_platform_error(e); pe == i32(win32.ERROR_NO_MORE_FILES) {
|
||||
it.impl.no_more_files = true
|
||||
}
|
||||
it.impl.no_more_files = true
|
||||
}
|
||||
if ok {
|
||||
return
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
TEMP_ALLOCATOR_GUARD()
|
||||
|
||||
@(require_results)
|
||||
_read_directory_iterator_create :: proc(f: ^File) -> (it: Read_Directory_Iterator, err: Error) {
|
||||
if f == nil {
|
||||
return
|
||||
}
|
||||
it.f = f
|
||||
impl := (^File_Impl)(f.impl)
|
||||
|
||||
if !is_directory(impl.name) {
|
||||
return nil, .Invalid_Dir
|
||||
}
|
||||
|
||||
n := n
|
||||
size := n
|
||||
if n <= 0 {
|
||||
n = -1
|
||||
size = 100
|
||||
err = .Invalid_Dir
|
||||
return
|
||||
}
|
||||
|
||||
wpath: []u16
|
||||
@@ -73,46 +98,31 @@ _read_directory :: proc(f: ^File, n: int, allocator: runtime.Allocator) -> (file
|
||||
wpath = impl.wname[:i]
|
||||
}
|
||||
|
||||
TEMP_ALLOCATOR_GUARD()
|
||||
|
||||
wpath_search := make([]u16, len(wpath)+3, context.temp_allocator)
|
||||
wpath_search := make([]u16, len(wpath)+3, temp_allocator())
|
||||
copy(wpath_search, wpath)
|
||||
wpath_search[len(wpath)+0] = '\\'
|
||||
wpath_search[len(wpath)+1] = '*'
|
||||
wpath_search[len(wpath)+2] = 0
|
||||
|
||||
find_data := &win32.WIN32_FIND_DATAW{}
|
||||
find_handle := win32.FindFirstFileW(raw_data(wpath_search), find_data)
|
||||
if find_handle == win32.INVALID_HANDLE_VALUE {
|
||||
return nil, _get_platform_error()
|
||||
it.impl.find_handle = win32.FindFirstFileW(raw_data(wpath_search), &it.impl.find_data)
|
||||
if it.impl.find_handle == win32.INVALID_HANDLE_VALUE {
|
||||
err = _get_platform_error()
|
||||
return
|
||||
}
|
||||
defer win32.FindClose(find_handle)
|
||||
|
||||
path := _cleanpath_from_buf(wpath, temp_allocator()) or_return
|
||||
|
||||
dfi := make([dynamic]File_Info, 0, size, allocator)
|
||||
defer if err != nil {
|
||||
for fi in dfi {
|
||||
file_info_delete(fi, allocator)
|
||||
}
|
||||
delete(dfi)
|
||||
}
|
||||
for n != 0 {
|
||||
fi: File_Info
|
||||
fi = find_data_to_file_info(path, find_data, allocator) or_return
|
||||
if fi.name != "" {
|
||||
append(&dfi, fi)
|
||||
n -= 1
|
||||
}
|
||||
|
||||
if !win32.FindNextFileW(find_handle, find_data) {
|
||||
e := _get_platform_error()
|
||||
if pe, _ := is_platform_error(e); pe == i32(win32.ERROR_NO_MORE_FILES) {
|
||||
break
|
||||
}
|
||||
return dfi[:], e
|
||||
}
|
||||
win32.FindClose(it.impl.find_handle)
|
||||
}
|
||||
|
||||
return dfi[:], nil
|
||||
it.impl.path = _cleanpath_from_buf(wpath, file_allocator()) or_return
|
||||
return
|
||||
}
|
||||
|
||||
_read_directory_iterator_destroy :: proc(it: ^Read_Directory_Iterator) {
|
||||
if it.f == nil {
|
||||
return
|
||||
}
|
||||
file_info_delete(it.impl.prev_fi, file_allocator())
|
||||
win32.FindClose(it.impl.find_handle)
|
||||
}
|
||||
@@ -106,7 +106,7 @@ open :: proc(name: string, flags := File_Flags{.Read}, perm := 0o777) -> (^File,
|
||||
|
||||
@(require_results)
|
||||
new_file :: proc(handle: uintptr, name: string) -> ^File {
|
||||
return _new_file(handle, name)
|
||||
return _new_file(handle, name) or_else panic("Out of memory")
|
||||
}
|
||||
|
||||
@(require_results)
|
||||
|
||||
@@ -102,21 +102,24 @@ _open :: proc(name: string, flags: File_Flags, perm: int) -> (f: ^File, err: Err
|
||||
return nil, _get_platform_error(errno)
|
||||
}
|
||||
|
||||
return _new_file(uintptr(fd), name), nil
|
||||
return _new_file(uintptr(fd), name)
|
||||
}
|
||||
|
||||
_new_file :: proc(fd: uintptr, _: string = "") -> ^File {
|
||||
impl := new(File_Impl, file_allocator())
|
||||
_new_file :: proc(fd: uintptr, _: string = "") -> (f: ^File, err: Error) {
|
||||
impl := new(File_Impl, file_allocator()) or_return
|
||||
defer if err != nil {
|
||||
free(impl, file_allocator())
|
||||
}
|
||||
impl.file.impl = impl
|
||||
impl.fd = linux.Fd(fd)
|
||||
impl.allocator = file_allocator()
|
||||
impl.name = _get_full_path(impl.fd, impl.allocator)
|
||||
impl.name = _get_full_path(impl.fd, file_allocator()) or_return
|
||||
impl.file.stream = {
|
||||
data = impl,
|
||||
procedure = _file_stream_proc,
|
||||
}
|
||||
impl.file.fstat = _fstat
|
||||
return &impl.file
|
||||
return &impl.file, nil
|
||||
}
|
||||
|
||||
_destroy :: proc(f: ^File_Impl) -> Error {
|
||||
|
||||
@@ -94,7 +94,7 @@ _open_internal :: proc(name: string, flags: File_Flags, perm: int) -> (handle: u
|
||||
create_mode = win32.TRUNCATE_EXISTING
|
||||
}
|
||||
|
||||
attrs: u32 = win32.FILE_ATTRIBUTE_NORMAL
|
||||
attrs: u32 = win32.FILE_ATTRIBUTE_NORMAL|win32.FILE_FLAG_BACKUP_SEMANTICS
|
||||
if perm & S_IWRITE == 0 {
|
||||
attrs = win32.FILE_ATTRIBUTE_READONLY
|
||||
if create_mode == win32.CREATE_ALWAYS {
|
||||
@@ -126,20 +126,24 @@ _open_internal :: proc(name: string, flags: File_Flags, perm: int) -> (handle: u
|
||||
_open :: proc(name: string, flags: File_Flags, perm: int) -> (f: ^File, err: Error) {
|
||||
flags := flags if flags != nil else {.Read}
|
||||
handle := _open_internal(name, flags, perm) or_return
|
||||
return _new_file(handle, name), nil
|
||||
return _new_file(handle, name)
|
||||
}
|
||||
|
||||
_new_file :: proc(handle: uintptr, name: string) -> ^File {
|
||||
_new_file :: proc(handle: uintptr, name: string) -> (f: ^File, err: Error) {
|
||||
if handle == INVALID_HANDLE {
|
||||
return nil
|
||||
return
|
||||
}
|
||||
impl := new(File_Impl, file_allocator())
|
||||
impl := new(File_Impl, file_allocator()) or_return
|
||||
defer if err != nil {
|
||||
free(impl, file_allocator())
|
||||
}
|
||||
|
||||
impl.file.impl = impl
|
||||
|
||||
impl.allocator = file_allocator()
|
||||
impl.fd = rawptr(handle)
|
||||
impl.name, _ = clone_string(name, impl.allocator)
|
||||
impl.wname, _ = win32_utf8_to_wstring(name, impl.allocator)
|
||||
impl.name = clone_string(name, impl.allocator) or_return
|
||||
impl.wname = win32_utf8_to_wstring(name, impl.allocator) or_return
|
||||
|
||||
handle := _handle(&impl.file)
|
||||
kind := File_Impl_Kind.File
|
||||
@@ -157,7 +161,7 @@ _new_file :: proc(handle: uintptr, name: string) -> ^File {
|
||||
}
|
||||
impl.file.fstat = _fstat
|
||||
|
||||
return &impl.file
|
||||
return &impl.file, nil
|
||||
}
|
||||
|
||||
_fd :: proc(f: ^File) -> uintptr {
|
||||
|
||||
@@ -17,7 +17,3 @@ heap_allocator_proc :: proc(allocator_data: rawptr, mode: runtime.Allocator_Mode
|
||||
old_memory: rawptr, old_size: int, loc := #caller_location) -> ([]byte, runtime.Allocator_Error) {
|
||||
return _heap_allocator_proc(allocator_data, mode, size, alignment, old_memory, old_size, loc)
|
||||
}
|
||||
|
||||
|
||||
@(private)
|
||||
error_allocator := heap_allocator
|
||||
|
||||
@@ -188,7 +188,7 @@ _set_working_directory :: proc(dir: string) -> Error {
|
||||
return _get_platform_error(linux.chdir(dir_cstr))
|
||||
}
|
||||
|
||||
_get_full_path :: proc(fd: linux.Fd, allocator: runtime.Allocator) -> string {
|
||||
_get_full_path :: proc(fd: linux.Fd, allocator: runtime.Allocator) -> (fullpath: string, err: Error) {
|
||||
PROC_FD_PATH :: "/proc/self/fd/"
|
||||
|
||||
buf: [32]u8
|
||||
@@ -196,10 +196,9 @@ _get_full_path :: proc(fd: linux.Fd, allocator: runtime.Allocator) -> string {
|
||||
|
||||
strconv.itoa(buf[len(PROC_FD_PATH):], int(fd))
|
||||
|
||||
fullpath: string
|
||||
err: Error
|
||||
if fullpath, err = _read_link_cstr(cstring(&buf[0]), allocator); err != nil || fullpath[0] != '/' {
|
||||
return ""
|
||||
delete(fullpath, allocator)
|
||||
fullpath = ""
|
||||
}
|
||||
return fullpath
|
||||
return
|
||||
}
|
||||
|
||||
@@ -1,21 +1,34 @@
|
||||
package os2
|
||||
|
||||
import "core:time"
|
||||
import "base:runtime"
|
||||
import "core:path/filepath"
|
||||
import "core:strings"
|
||||
import "core:time"
|
||||
|
||||
Fstat_Callback :: proc(f: ^File, allocator: runtime.Allocator) -> (File_Info, Error)
|
||||
|
||||
File_Info :: struct {
|
||||
fullpath: string,
|
||||
name: string,
|
||||
|
||||
inode: u128, // might be zero if cannot be determined
|
||||
size: i64,
|
||||
mode: int,
|
||||
type: File_Type,
|
||||
|
||||
creation_time: time.Time,
|
||||
modification_time: time.Time,
|
||||
access_time: time.Time,
|
||||
}
|
||||
|
||||
@(require_results)
|
||||
file_info_clone :: proc(fi: File_Info, allocator: runtime.Allocator) -> (cloned: File_Info, err: runtime.Allocator_Error) {
|
||||
cloned = fi
|
||||
cloned.fullpath = strings.clone(fi.fullpath) or_return
|
||||
cloned.name = filepath.base(cloned.fullpath)
|
||||
return
|
||||
}
|
||||
|
||||
file_info_slice_delete :: proc(infos: []File_Info, allocator: runtime.Allocator) {
|
||||
for i := len(infos)-1; i >= 0; i -= 1 {
|
||||
file_info_delete(infos[i], allocator)
|
||||
|
||||
@@ -11,7 +11,7 @@ _fstat :: proc(f: ^File, allocator: runtime.Allocator) -> (File_Info, Error) {
|
||||
return _fstat_internal(impl.fd, allocator)
|
||||
}
|
||||
|
||||
_fstat_internal :: proc(fd: linux.Fd, allocator: runtime.Allocator) -> (File_Info, Error) {
|
||||
_fstat_internal :: proc(fd: linux.Fd, allocator: runtime.Allocator) -> (fi: File_Info, err: Error) {
|
||||
s: linux.Stat
|
||||
errno := linux.fstat(fd, &s)
|
||||
if errno != .NONE {
|
||||
@@ -28,20 +28,22 @@ _fstat_internal :: proc(fd: linux.Fd, allocator: runtime.Allocator) -> (File_Inf
|
||||
case linux.S_IFSOCK: type = .Socket
|
||||
}
|
||||
mode := int(0o7777 & transmute(u32)s.mode)
|
||||
|
||||
// TODO: As of Linux 4.11, the new statx syscall can retrieve creation_time
|
||||
fi := File_Info {
|
||||
fullpath = _get_full_path(fd, allocator),
|
||||
name = "",
|
||||
size = i64(s.size),
|
||||
mode = mode,
|
||||
type = type,
|
||||
fi = File_Info {
|
||||
fullpath = _get_full_path(fd, allocator) or_return,
|
||||
name = "",
|
||||
inode = u128(u64(s.ino)),
|
||||
size = i64(s.size),
|
||||
mode = mode,
|
||||
type = type,
|
||||
modification_time = time.Time {i64(s.mtime.time_sec) * i64(time.Second) + i64(s.mtime.time_nsec)},
|
||||
access_time = time.Time {i64(s.atime.time_sec) * i64(time.Second) + i64(s.atime.time_nsec)},
|
||||
creation_time = time.Time{i64(s.ctime.time_sec) * i64(time.Second) + i64(s.ctime.time_nsec)}, // regular stat does not provide this
|
||||
access_time = time.Time {i64(s.atime.time_sec) * i64(time.Second) + i64(s.atime.time_nsec)},
|
||||
creation_time = time.Time{i64(s.ctime.time_sec) * i64(time.Second) + i64(s.ctime.time_nsec)}, // regular stat does not provide this
|
||||
}
|
||||
fi.creation_time = fi.modification_time
|
||||
fi.name = filepath.base(fi.fullpath)
|
||||
return fi, nil
|
||||
return
|
||||
}
|
||||
|
||||
// NOTE: _stat and _lstat are using _fstat to avoid a race condition when populating fullpath
|
||||
|
||||
@@ -262,7 +262,8 @@ _file_info_from_get_file_information_by_handle :: proc(path: string, h: win32.HA
|
||||
fi: File_Info
|
||||
fi.fullpath = path
|
||||
fi.name = basename(path)
|
||||
fi.size = i64(d.nFileSizeHigh)<<32 + i64(d.nFileSizeLow)
|
||||
fi.inode = u128(u64(d.nFileIndexHigh)<<32 + u64(d.nFileIndexLow))
|
||||
fi.size = i64(d.nFileSizeHigh)<<32 + i64(d.nFileSizeLow)
|
||||
type, mode := _file_type_mode_from_file_attributes(d.dwFileAttributes, nil, 0)
|
||||
fi.type = type
|
||||
fi.mode |= mode
|
||||
|
||||
@@ -15,10 +15,27 @@ foreign ntdll_lib {
|
||||
ProcessInformationLength: u32,
|
||||
ReturnLength: ^u32,
|
||||
) -> u32 ---
|
||||
|
||||
NtQueryInformationFile :: proc(
|
||||
FileHandle: HANDLE,
|
||||
IoStatusBlock: PIO_STATUS_BLOCK,
|
||||
FileInformation: rawptr,
|
||||
Length: ULONG,
|
||||
FileInformationClass: FILE_INFORMATION_CLASS,
|
||||
) -> NTSTATUS ---
|
||||
}
|
||||
|
||||
PIO_STATUS_BLOCK :: ^IO_STATUS_BLOCK
|
||||
IO_STATUS_BLOCK :: struct {
|
||||
using _: struct #raw_union {
|
||||
Status: NTSTATUS,
|
||||
Pointer: rawptr,
|
||||
},
|
||||
Information: ULONG_PTR,
|
||||
}
|
||||
|
||||
|
||||
PROCESS_INFO_CLASS :: enum i32 {
|
||||
PROCESS_INFO_CLASS :: enum c_int {
|
||||
ProcessBasicInformation = 0,
|
||||
ProcessDebugPort = 7,
|
||||
ProcessWow64Information = 26,
|
||||
@@ -29,6 +46,95 @@ PROCESS_INFO_CLASS :: enum i32 {
|
||||
}
|
||||
|
||||
|
||||
PFILE_INFORMATION_CLASS :: ^FILE_INFORMATION_CLASS
|
||||
FILE_INFORMATION_CLASS :: enum c_int {
|
||||
FileDirectoryInformation = 1,
|
||||
FileFullDirectoryInformation = 2,
|
||||
FileBothDirectoryInformation = 3,
|
||||
FileBasicInformation = 4,
|
||||
FileStandardInformation = 5,
|
||||
FileInternalInformation = 6,
|
||||
FileEaInformation = 7,
|
||||
FileAccessInformation = 8,
|
||||
FileNameInformation = 9,
|
||||
FileRenameInformation = 10,
|
||||
FileLinkInformation = 11,
|
||||
FileNamesInformation = 12,
|
||||
FileDispositionInformation = 13,
|
||||
FilePositionInformation = 14,
|
||||
FileFullEaInformation = 15,
|
||||
FileModeInformation = 16,
|
||||
FileAlignmentInformation = 17,
|
||||
FileAllInformation = 18,
|
||||
FileAllocationInformation = 19,
|
||||
FileEndOfFileInformation = 20,
|
||||
FileAlternateNameInformation = 21,
|
||||
FileStreamInformation = 22,
|
||||
FilePipeInformation = 23,
|
||||
FilePipeLocalInformation = 24,
|
||||
FilePipeRemoteInformation = 25,
|
||||
FileMailslotQueryInformation = 26,
|
||||
FileMailslotSetInformation = 27,
|
||||
FileCompressionInformation = 28,
|
||||
FileObjectIdInformation = 29,
|
||||
FileCompletionInformation = 30,
|
||||
FileMoveClusterInformation = 31,
|
||||
FileQuotaInformation = 32,
|
||||
FileReparsePointInformation = 33,
|
||||
FileNetworkOpenInformation = 34,
|
||||
FileAttributeTagInformation = 35,
|
||||
FileTrackingInformation = 36,
|
||||
FileIdBothDirectoryInformation = 37,
|
||||
FileIdFullDirectoryInformation = 38,
|
||||
FileValidDataLengthInformation = 39,
|
||||
FileShortNameInformation = 40,
|
||||
FileIoCompletionNotificationInformation = 41,
|
||||
FileIoStatusBlockRangeInformation = 42,
|
||||
FileIoPriorityHintInformation = 43,
|
||||
FileSfioReserveInformation = 44,
|
||||
FileSfioVolumeInformation = 45,
|
||||
FileHardLinkInformation = 46,
|
||||
FileProcessIdsUsingFileInformation = 47,
|
||||
FileNormalizedNameInformation = 48,
|
||||
FileNetworkPhysicalNameInformation = 49,
|
||||
FileIdGlobalTxDirectoryInformation = 50,
|
||||
FileIsRemoteDeviceInformation = 51,
|
||||
FileUnusedInformation = 52,
|
||||
FileNumaNodeInformation = 53,
|
||||
FileStandardLinkInformation = 54,
|
||||
FileRemoteProtocolInformation = 55,
|
||||
FileRenameInformationBypassAccessCheck = 56,
|
||||
FileLinkInformationBypassAccessCheck = 57,
|
||||
FileVolumeNameInformation = 58,
|
||||
FileIdInformation = 59,
|
||||
FileIdExtdDirectoryInformation = 60,
|
||||
FileReplaceCompletionInformation = 61,
|
||||
FileHardLinkFullIdInformation = 62,
|
||||
FileIdExtdBothDirectoryInformation = 63,
|
||||
FileDispositionInformationEx = 64,
|
||||
FileRenameInformationEx = 65,
|
||||
FileRenameInformationExBypassAccessCheck = 66,
|
||||
FileDesiredStorageClassInformation = 67,
|
||||
FileStatInformation = 68,
|
||||
FileMemoryPartitionInformation = 69,
|
||||
FileStatLxInformation = 70,
|
||||
FileCaseSensitiveInformation = 71,
|
||||
FileLinkInformationEx = 72,
|
||||
FileLinkInformationExBypassAccessCheck = 73,
|
||||
FileStorageReserveIdInformation = 74,
|
||||
FileCaseSensitiveInformationForceAccessCheck = 75,
|
||||
FileKnownFolderInformation = 76,
|
||||
FileStatBasicInformation = 77,
|
||||
FileId64ExtdDirectoryInformation = 78,
|
||||
FileId64ExtdBothDirectoryInformation = 79,
|
||||
FileIdAllExtdDirectoryInformation = 80,
|
||||
FileIdAllExtdBothDirectoryInformation = 81,
|
||||
FileStreamReservationInformation,
|
||||
FileMupProviderInfo,
|
||||
FileMaximumInformation,
|
||||
}
|
||||
|
||||
|
||||
|
||||
PROCESS_BASIC_INFORMATION :: struct {
|
||||
ExitStatus: NTSTATUS,
|
||||
|
||||
@@ -1027,16 +1027,28 @@ TRACKMOUSEEVENT :: struct {
|
||||
}
|
||||
|
||||
WIN32_FIND_DATAW :: struct {
|
||||
dwFileAttributes: DWORD,
|
||||
ftCreationTime: FILETIME,
|
||||
ftLastAccessTime: FILETIME,
|
||||
ftLastWriteTime: FILETIME,
|
||||
nFileSizeHigh: DWORD,
|
||||
nFileSizeLow: DWORD,
|
||||
dwReserved0: DWORD,
|
||||
dwReserved1: DWORD,
|
||||
cFileName: [260]wchar_t, // #define MAX_PATH 260
|
||||
cAlternateFileName: [14]wchar_t,
|
||||
dwFileAttributes: DWORD,
|
||||
ftCreationTime: FILETIME,
|
||||
ftLastAccessTime: FILETIME,
|
||||
ftLastWriteTime: FILETIME,
|
||||
nFileSizeHigh: DWORD,
|
||||
nFileSizeLow: DWORD,
|
||||
dwReserved0: DWORD,
|
||||
dwReserved1: DWORD,
|
||||
cFileName: [MAX_PATH]WCHAR,
|
||||
cAlternateFileName: [14]WCHAR,
|
||||
_OBSOLETE_dwFileType: DWORD, // Obsolete. Do not use.
|
||||
_OBSOLETE_dwCreatorType: DWORD, // Obsolete. Do not use
|
||||
_OBSOLETE_wFinderFlags: WORD, // Obsolete. Do not use
|
||||
}
|
||||
|
||||
FILE_ID_128 :: struct {
|
||||
Identifier: [16]BYTE,
|
||||
}
|
||||
|
||||
FILE_ID_INFO :: struct {
|
||||
VolumeSerialNumber: ULONGLONG,
|
||||
FileId: FILE_ID_128,
|
||||
}
|
||||
|
||||
CREATESTRUCTA :: struct {
|
||||
@@ -2714,41 +2726,41 @@ NEON128 :: struct {
|
||||
|
||||
EXCEPTION_POINTERS :: struct {
|
||||
ExceptionRecord: ^EXCEPTION_RECORD,
|
||||
ContextRecord: ^CONTEXT,
|
||||
ContextRecord: ^CONTEXT,
|
||||
}
|
||||
|
||||
PVECTORED_EXCEPTION_HANDLER :: #type proc "system" (ExceptionInfo: ^EXCEPTION_POINTERS) -> LONG
|
||||
|
||||
CONSOLE_READCONSOLE_CONTROL :: struct {
|
||||
nLength: ULONG,
|
||||
nInitialChars: ULONG,
|
||||
dwCtrlWakeupMask: ULONG,
|
||||
nLength: ULONG,
|
||||
nInitialChars: ULONG,
|
||||
dwCtrlWakeupMask: ULONG,
|
||||
dwControlKeyState: ULONG,
|
||||
}
|
||||
|
||||
PCONSOLE_READCONSOLE_CONTROL :: ^CONSOLE_READCONSOLE_CONTROL
|
||||
|
||||
BY_HANDLE_FILE_INFORMATION :: struct {
|
||||
dwFileAttributes: DWORD,
|
||||
ftCreationTime: FILETIME,
|
||||
ftLastAccessTime: FILETIME,
|
||||
ftLastWriteTime: FILETIME,
|
||||
dwFileAttributes: DWORD,
|
||||
ftCreationTime: FILETIME,
|
||||
ftLastAccessTime: FILETIME,
|
||||
ftLastWriteTime: FILETIME,
|
||||
dwVolumeSerialNumber: DWORD,
|
||||
nFileSizeHigh: DWORD,
|
||||
nFileSizeLow: DWORD,
|
||||
nNumberOfLinks: DWORD,
|
||||
nFileIndexHigh: DWORD,
|
||||
nFileIndexLow: DWORD,
|
||||
nFileSizeHigh: DWORD,
|
||||
nFileSizeLow: DWORD,
|
||||
nNumberOfLinks: DWORD,
|
||||
nFileIndexHigh: DWORD,
|
||||
nFileIndexLow: DWORD,
|
||||
}
|
||||
|
||||
LPBY_HANDLE_FILE_INFORMATION :: ^BY_HANDLE_FILE_INFORMATION
|
||||
|
||||
FILE_STANDARD_INFO :: struct {
|
||||
AllocationSize: LARGE_INTEGER,
|
||||
EndOfFile: LARGE_INTEGER,
|
||||
NumberOfLinks: DWORD,
|
||||
DeletePending: BOOLEAN,
|
||||
Directory: BOOLEAN,
|
||||
EndOfFile: LARGE_INTEGER,
|
||||
NumberOfLinks: DWORD,
|
||||
DeletePending: BOOLEAN,
|
||||
Directory: BOOLEAN,
|
||||
}
|
||||
|
||||
FILE_ATTRIBUTE_TAG_INFO :: struct {
|
||||
|
||||
@@ -6,6 +6,7 @@ import "base:runtime"
|
||||
import "core:bytes"
|
||||
import "core:encoding/ansi"
|
||||
@require import "core:encoding/base64"
|
||||
@require import "core:encoding/json"
|
||||
import "core:fmt"
|
||||
import "core:io"
|
||||
@require import pkg_log "core:log"
|
||||
@@ -44,7 +45,8 @@ SHARED_RANDOM_SEED : u64 : #config(ODIN_TEST_RANDOM_SEED, 0)
|
||||
LOG_LEVEL : string : #config(ODIN_TEST_LOG_LEVEL, "info")
|
||||
// Show only the most necessary logging information.
|
||||
USING_SHORT_LOGS : bool : #config(ODIN_TEST_SHORT_LOGS, false)
|
||||
|
||||
// Output a report of the tests to the given path.
|
||||
JSON_REPORT : string : #config(ODIN_TEST_JSON_REPORT, "")
|
||||
|
||||
get_log_level :: #force_inline proc() -> runtime.Logger_Level {
|
||||
when ODIN_DEBUG {
|
||||
@@ -61,6 +63,18 @@ get_log_level :: #force_inline proc() -> runtime.Logger_Level {
|
||||
}
|
||||
}
|
||||
|
||||
JSON :: struct {
|
||||
total: int,
|
||||
success: int,
|
||||
duration: time.Duration,
|
||||
packages: map[string][dynamic]JSON_Test,
|
||||
}
|
||||
|
||||
JSON_Test :: struct {
|
||||
success: bool,
|
||||
name: string,
|
||||
}
|
||||
|
||||
end_t :: proc(t: ^T) {
|
||||
for i := len(t.cleanups)-1; i >= 0; i -= 1 {
|
||||
#no_bounds_check c := t.cleanups[i]
|
||||
@@ -847,5 +861,35 @@ To partly mitigate this, redirect STDERR to a file or use the -define:ODIN_TEST_
|
||||
|
||||
fmt.wprintln(stderr, bytes.buffer_to_string(&batch_buffer))
|
||||
|
||||
when JSON_REPORT != "" {
|
||||
json_report: JSON
|
||||
|
||||
mode: int
|
||||
when ODIN_OS != .Windows {
|
||||
mode = os.S_IRUSR|os.S_IWUSR|os.S_IRGRP|os.S_IROTH
|
||||
}
|
||||
json_fd, errno := os.open(JSON_REPORT, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, mode)
|
||||
fmt.assertf(errno == os.ERROR_NONE, "unable to open file %q for writing of JSON report, error: %v", JSON_REPORT, errno)
|
||||
defer os.close(json_fd)
|
||||
|
||||
for test, i in report.all_tests {
|
||||
#no_bounds_check state := report.all_test_states[i]
|
||||
|
||||
if test.pkg not_in json_report.packages {
|
||||
json_report.packages[test.pkg] = {}
|
||||
}
|
||||
|
||||
tests := &json_report.packages[test.pkg]
|
||||
append(tests, JSON_Test{name = test.name, success = state == .Successful})
|
||||
}
|
||||
|
||||
json_report.total = len(internal_tests)
|
||||
json_report.success = total_success_count
|
||||
json_report.duration = finished_in
|
||||
|
||||
err := json.marshal_to_writer(os.stream_from_handle(json_fd), json_report, &{ pretty = true })
|
||||
fmt.assertf(err == nil, "Error writing JSON report: %v", err)
|
||||
}
|
||||
|
||||
return total_success_count == total_test_count
|
||||
}
|
||||
|
||||
@@ -396,7 +396,7 @@ gb_internal LLVMValueRef lb_run_instrumentation_pass_insert_call(lbProcedure *p,
|
||||
lbValue cc = lb_find_procedure_value_from_entity(m, entity);
|
||||
|
||||
LLVMValueRef args[3] = {};
|
||||
args[0] = p->value;
|
||||
args[0] = LLVMConstPointerCast(p->value, lb_type(m, t_rawptr));
|
||||
|
||||
if (is_arch_wasm()) {
|
||||
args[1] = LLVMConstPointerNull(lb_type(m, t_rawptr));
|
||||
|
||||
Reference in New Issue
Block a user