mirror of
https://github.com/odin-lang/Odin.git
synced 2025-12-28 08:54:35 +00:00
519 lines
9.3 KiB
Odin
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
|
|
}
|