Files
Odin/vendor/libc/stdio.odin
Laytan Laats 140c902eff vendor/libc: a bunch of additions
All these additions are to allow Dear ImGui to be compiled natively.
2025-03-12 19:25:35 +01:00

519 lines
9.3 KiB
Odin

#+build !freestanding
package odin_libc
import "base:runtime"
import "core:c"
import "core:io"
import "core:os"
import "core:strconv"
import stb "vendor:stb/sprintf"
FILE :: uintptr
EOF :: -1
@(require, linkage="strong", link_name="fopen")
fopen :: proc "c" (path: cstring, mode: cstring) -> FILE {
context = g_ctx
unimplemented("vendor/libc: fopen")
}
@(require, linkage="strong", link_name="fseek")
fseek :: proc "c" (file: FILE, offset: c.long, whence: i32) -> i32 {
context = g_ctx
handle := os.Handle(file-1)
_, err := os.seek(handle, i64(offset), int(whence))
if err != nil {
return -1
}
return 0
}
@(require, linkage="strong", link_name="ftell")
ftell :: proc "c" (file: FILE) -> c.long {
context = g_ctx
handle := os.Handle(file-1)
off, err := os.seek(handle, 0, os.SEEK_CUR)
if err != nil {
return -1
}
return c.long(off)
}
@(require, linkage="strong", link_name="fclose")
fclose :: proc "c" (file: FILE) -> i32 {
context = g_ctx
handle := os.Handle(file-1)
if os.close(handle) != nil {
return -1
}
return 0
}
@(require, linkage="strong", link_name="fread")
fread :: proc "c" (buffer: [^]byte, size: uint, count: uint, file: FILE) -> uint {
context = g_ctx
handle := os.Handle(file-1)
n, _ := os.read(handle, buffer[:min(size, count)])
return uint(max(0, n))
}
@(require, linkage="strong", link_name="fwrite")
fwrite :: proc "c" (buffer: [^]byte, size: uint, count: uint, file: FILE) -> uint {
context = g_ctx
handle := os.Handle(file-1)
n, _ := os.write(handle, buffer[:min(size, count)])
return uint(max(0, n))
}
@(require, linkage="strong", link_name="putchar")
putchar :: proc "c" (char: c.int) -> c.int {
context = g_ctx
n, err := os.write_byte(os.stdout, byte(char))
if n == 0 || err != nil {
return EOF
}
return char
}
@(require, linkage="strong", link_name="getchar")
getchar :: proc "c" () -> c.int {
when #defined(os.stdin) {
ret: [1]byte
n, err := os.read(os.stdin, ret[:])
if n == 0 || err != nil {
return EOF
}
return c.int(ret[0])
} else {
return EOF
}
}
@(require, linkage="strong", link_name="vsnprintf")
vsnprintf :: proc "c" (buf: [^]byte, count: uint, fmt: cstring, args: ^c.va_list) -> i32 {
i32_count := i32(count)
assert_contextless(i32_count >= 0)
return stb.vsnprintf(buf, i32_count, fmt, args)
}
@(require, linkage="strong", link_name="vsprintf")
vsprintf :: proc "c" (buf: [^]byte, fmt: cstring, args: ^c.va_list) -> i32 {
return stb.vsprintf(buf, fmt, args)
}
@(require, linkage="strong", link_name="vfprintf")
vfprintf :: proc "c" (file: FILE, fmt: cstring, args: ^c.va_list) -> i32 {
context = g_ctx
handle := os.Handle(file-1)
MAX_STACK :: 4096
buf: []byte
stack_buf: [MAX_STACK]byte = ---
{
n := stb.vsnprintf(&stack_buf[0], MAX_STACK, fmt, args)
if n <= 0 {
return n
}
if n >= MAX_STACK {
buf = make([]byte, n)
n2 := stb.vsnprintf(raw_data(buf), i32(len(buf)), fmt, args)
assert(n == n2)
} else {
buf = stack_buf[:n]
}
}
defer if len(buf) > MAX_STACK {
delete(buf)
}
_, err := io.write_full(os.stream_from_handle(handle), buf)
if err != nil {
return -1
}
return i32(len(buf))
}
/*
Derived from musl libc - MIT licensed - Copyright © 2005-2020 Rich Felker, et al.
*/
@(require, linkage="strong", link_name="__sscanf")
_sscanf :: proc "c" (str, fmt: [^]byte, orig_ptrs: [^]rawptr) -> i32 {
Size :: enum u8 {
None,
hh,
h,
l,
L,
ll,
}
store_int :: proc(dest: rawptr, size: Size, i: u64) {
if dest == nil { return }
#partial switch size {
case .hh:
(^c.char)(dest)^ = c.char(i)
case .h:
(^c.short)(dest)^ = c.short(i)
case .None:
(^c.int)(dest)^ = c.int(i)
case .l:
(^c.long)(dest)^ = c.long(i)
case .ll:
(^c.longlong)(dest)^ = c.longlong(i)
}
}
context = g_ctx
str := str
ptrs := orig_ptrs
// TODO: implement wide char variants
pos: u64
dest: rawptr
ch, t: byte
// wcs: [^]c.wchar_t
s: [^]byte
k, i, width: int
alloc: bool
scanset: [257]byte
invert: u8
matches: i32
size: Size
input_fail, match_fail, fmt_fail, alloc_fail: bool
main_loop: for p := fmt; p[0] != 0; p = p[1:] {
alloc = false
if isspace(i32(p[0])) {
for isspace(i32(p[0])) {
p = p[1:]
}
for isspace(i32(str[0])) {
str = str[1:]
pos += 1
}
}
if p[0] != '%' || p[1] == '%' {
if p[0] == '%' {
p = p[1:]
}
ch = str[0]
if ch != p[0] {
if ch == 0 {
input_fail = true
break
}
match_fail = true
break
}
pos += 1
continue
}
p = p[1:]
if p[0] == '*' {
dest = nil
p = p[1:]
} else if isdigit(i32(p[0])) && p[1] == '$' {
dest = orig_ptrs[p[0] - '0']
p = p[2:]
} else {
dest = ptrs[0]
ptrs = ptrs[1:]
}
for width = 0; isdigit(i32(p[0])); p = p[1:] {
width = 10 * width + int(p[0] - '0')
}
if p[0] == 'm' {
// wcs = nil
s = nil
alloc = dest != nil
p = p[1:]
} else {
alloc = false
}
size = .None
p = p[1:]
switch p[-1] {
case 'h':
size = .h
if p[0] == 'h' {
p = p[1:]
size = .hh
}
case 'l':
size = .l
if p[0] == 'l' {
p = p[1:]
size = .ll
}
case 'j':
size = .ll
case 'z', 't':
size = .l
case 'L':
size = .L
case 'd', 'i', 'o', 'u', 'x',
'a', 'e', 'f', 'g',
'A', 'E', 'F', 'G', 'X',
's', 'c', '[',
'S', 'C', 'p', 'n':
p = p[-1:]
case:
fmt_fail = true
break main_loop
}
t = p[0]
switch t {
case 'C':
t = 'c'
size = .l
case 'S':
t = 's'
size = .l
}
switch t {
case 'c':
if width < 1 {
width = 1
}
case '[':
case 'n':
store_int(dest, size, pos)
continue
case:
for isspace(i32(str[0])) {
str = str[1:]
pos += 1
}
}
if str[0] == 0 {
input_fail = true
break
}
if width == 0 {
width = max(int)
}
switch t {
case 's', 'c', '[':
if t == 'c' || t == 's' {
runtime.memset(&scanset, -1, size_of(scanset))
scanset[0] = 0
if t == 's' {
scanset['\t'] = 0
scanset['\n'] = 0
scanset['\v'] = 0
scanset['\f'] = 0
scanset['\r'] = 0
scanset[' '] = 0
}
} else {
p = p[1:]
invert = 0
if p[0] == '^' {
p = p[1:]
invert = 1
}
runtime.memset(&scanset, i32(invert), size_of(scanset))
scanset[0] = 0
if p[0] == '-' {
p = p[1:]
scanset['-'] = 1 - invert
} else if p[0] == ']' {
p = p[1:]
scanset[']'] = 1 - invert
}
for ; p[0] != ']'; p = p[1:] {
if p[0] == 0 {
fmt_fail = true
break main_loop
}
if p[0] == '-' && p[1] != ']' {
c := p
p = p[1:]
for ch = c[0]; c[0] < p[0]; c, ch = c[1:], c[0] {
scanset[ch] = 1 - invert
}
scanset[p[0]] = 1 - invert
}
}
}
// wcs = nil
s = nil
i = 0
k = t == 'c' ? width + 1 : 31
if size == .l {
unimplemented("vendor/libc: sscanf wide character support")
} else if alloc {
s = make([^]byte, k)
if s == nil {
alloc_fail = true
break main_loop
}
for ch = str[0]; scanset[ch] != 0 && i < width; {
s[i] = ch
i += 1
if i == k {
old_size := k
k += k + 1
tmp, _ := runtime.non_zero_mem_resize(s, old_size, k)
if tmp == nil {
alloc_fail = true
break main_loop
}
s = raw_data(tmp)
}
str = str[1:]
ch = str[0]
}
} else {
s = cast([^]byte)dest
if s != nil {
for ch = str[0]; scanset[ch] != 0 && i < width; {
s[i] = ch
i += 1
str = str[1:]
ch = str[0]
}
} else {
for ; scanset[str[0]] != 0 && i < width; str = str[1:] {}
}
}
if i == 0 {
match_fail = true
break main_loop
}
str = str[-1:]
if t == 'c' && i != width {
match_fail = true
break main_loop
}
if alloc {
(^rawptr)(dest)^ = s
}
if t != 'c' {
if s != nil {s[i] = 0}
}
case:
base := -1
switch t {
case 'p', 'X', 'x':
base = 16
if i + 2 < width && str[0] == '0' && str[1] == 'x' {
str = str[2:]
}
case 'o':
base = 8
if i + 1 < width && str[0] == '0' {
str = str[1:]
}
case 'd', 'u':
base = 10
case 'i':
base = 0
}
odin_str := string(cstring(str))
odin_str = odin_str[:min(len(odin_str), width-i)]
cnt: int
if base >= 0 {
x: i64
if base == 0 {
x, _ = strconv.parse_i64_maybe_prefixed(odin_str, &cnt)
} else {
x, _ = strconv.parse_i64_of_base(odin_str, base, &cnt)
}
if cnt == 0 {
match_fail = true
break main_loop
}
if t == 'p' && dest != nil {
(^rawptr)(dest)^ = rawptr(uintptr(x))
} else {
store_int(dest, size, u64(x))
}
} else {
// should be a guarantee bcs of validation above.
// switch t {
// case 'a', 'A',
// 'e', 'E',
// 'f', 'F',
// 'g', 'G':
// }
x, _ := strconv.parse_f64(odin_str, &cnt)
if cnt == 0 {
match_fail = true
break main_loop
}
if dest != nil {
#partial switch size {
case .None:
(^c.float)(dest)^ = c.float(x)
case .l:
(^c.double)(dest)^ = c.double(x)
case .L:
(^c.double)(dest)^ = c.double(x) // longdouble
}
}
}
pos += u64(cnt)
str = str[cnt:]
}
if dest != nil {
matches += 1
}
}
if fmt_fail || alloc_fail || input_fail {
if matches == 0 {
matches = -1
}
}
if match_fail {
if alloc {
free(s)
// free(wcs)
}
}
return matches
}