From ce379d033a3eec51dcfa37ddd0c8f869ec7e3a3b Mon Sep 17 00:00:00 2001 From: Jeroen van Rijn Date: Sun, 23 Aug 2026 21:42:58 +0200 Subject: [PATCH] Deprecate `core:os/old` as advertised. --- core/os/old/dir_unix.odin | 65 -- core/os/old/dir_windows.odin | 114 --- core/os/old/doc.odin | 2 - core/os/old/env_windows.odin | 140 ---- core/os/old/errors.odin | 318 ------- core/os/old/os.odin | 266 ------ core/os/old/os_darwin.odin | 1348 ------------------------------ core/os/old/os_freebsd.odin | 982 ---------------------- core/os/old/os_freestanding.odin | 4 - core/os/old/os_js.odin | 275 ------ core/os/old/os_linux.odin | 1222 --------------------------- core/os/old/os_netbsd.odin | 1032 ----------------------- core/os/old/os_openbsd.odin | 932 --------------------- core/os/old/os_wasi.odin | 273 ------ core/os/old/os_windows.odin | 871 ------------------- core/os/old/stat.odin | 33 - core/os/old/stat_unix.odin | 134 --- core/os/old/stat_windows.odin | 303 ------- core/os/old/stream.odin | 77 -- 19 files changed, 8391 deletions(-) delete mode 100644 core/os/old/dir_unix.odin delete mode 100644 core/os/old/dir_windows.odin delete mode 100644 core/os/old/doc.odin delete mode 100644 core/os/old/env_windows.odin delete mode 100644 core/os/old/errors.odin delete mode 100644 core/os/old/os.odin delete mode 100644 core/os/old/os_darwin.odin delete mode 100644 core/os/old/os_freebsd.odin delete mode 100644 core/os/old/os_freestanding.odin delete mode 100644 core/os/old/os_js.odin delete mode 100644 core/os/old/os_linux.odin delete mode 100644 core/os/old/os_netbsd.odin delete mode 100644 core/os/old/os_openbsd.odin delete mode 100644 core/os/old/os_wasi.odin delete mode 100644 core/os/old/os_windows.odin delete mode 100644 core/os/old/stat.odin delete mode 100644 core/os/old/stat_unix.odin delete mode 100644 core/os/old/stat_windows.odin delete mode 100644 core/os/old/stream.odin diff --git a/core/os/old/dir_unix.odin b/core/os/old/dir_unix.odin deleted file mode 100644 index 78115cbb9..000000000 --- a/core/os/old/dir_unix.odin +++ /dev/null @@ -1,65 +0,0 @@ -#+build darwin, linux, netbsd, freebsd, openbsd -package os_old - -import "core:strings" - -@(require_results) -read_dir :: proc(fd: Handle, n: int, allocator := context.allocator) -> (fi: []File_Info, err: Error) { - context.allocator = allocator - - dupfd := _dup(fd) or_return - dirp := _fdopendir(dupfd) or_return - defer _closedir(dirp) - - dirpath := absolute_path_from_handle(dupfd) or_return - defer delete(dirpath) - - n := n - size := n - if n <= 0 { - n = -1 - size = 100 - } - - dfi := make([dynamic]File_Info, 0, size, allocator) or_return - defer if err != nil { - for fi_ in dfi { - file_info_delete(fi_, allocator) - } - delete(dfi) - } - - for { - entry: Dirent - end_of_stream: bool - entry, err, end_of_stream = _readdir(dirp) - if err != nil { - return - } else if end_of_stream { - break - } - - fi_: File_Info - filename := string(cstring(&entry.name[0])) - - if filename == "." || filename == ".." { - continue - } - - fullpath := strings.join({ dirpath, filename }, "/", allocator) - - s: OS_Stat - s, err = _lstat(fullpath) - if err != nil { - delete(fullpath, allocator) - return - } - _fill_file_info_from_stat(&fi_, s) - fi_.fullpath = fullpath - fi_.name = path_base(fi_.fullpath) - - append(&dfi, fi_) - } - - return dfi[:], nil -} diff --git a/core/os/old/dir_windows.odin b/core/os/old/dir_windows.odin deleted file mode 100644 index b81787872..000000000 --- a/core/os/old/dir_windows.odin +++ /dev/null @@ -1,114 +0,0 @@ -package os_old - -import win32 "core:sys/windows" -import "core:strings" -import "base:runtime" - -@(require_results) -read_dir :: proc(fd: Handle, n: int, allocator := context.allocator) -> (fi: []File_Info, err: Error) { - @(require_results) - find_data_to_file_info :: proc(base_path: string, d: ^win32.WIN32_FIND_DATAW) -> (fi: File_Info) { - // Ignore "." and ".." - if d.cFileName[0] == '.' && d.cFileName[1] == 0 { - return - } - if d.cFileName[0] == '.' && d.cFileName[1] == '.' && d.cFileName[2] == 0 { - return - } - path := strings.concatenate({base_path, `\`, win32.utf16_to_utf8(d.cFileName[:]) or_else ""}) - fi.fullpath = path - fi.name = basename(path) - fi.size = i64(d.nFileSizeHigh)<<32 + i64(d.nFileSizeLow) - - 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 - } - - if is_sym { - fi.mode |= File_Mode_Sym_Link - } else { - if d.dwFileAttributes & win32.FILE_ATTRIBUTE_DIRECTORY != 0 { - fi.mode |= 0o111 | File_Mode_Dir - } - - // fi.mode |= file_type_mode(h); - } - - windows_set_file_info_times(&fi, d) - - fi.is_dir = fi.mode & File_Mode_Dir != 0 - return - } - - if fd == 0 { - return nil, ERROR_INVALID_HANDLE - } - - context.allocator = allocator - - h := win32.HANDLE(fd) - - dir_fi, _ := file_info_from_get_file_information_by_handle("", h) - if !dir_fi.is_dir { - return nil, .Not_Dir - } - - n := n - size := n - if n <= 0 { - n = -1 - size = 100 - } - runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD(ignore = context.temp_allocator == allocator) - - wpath := cleanpath_from_handle_u16(fd, context.temp_allocator) or_return - if len(wpath) == 0 { - return - } - - dfi := make([dynamic]File_Info, 0, size) or_return - - wpath_search := make([]u16, len(wpath)+3, context.temp_allocator) or_return - copy(wpath_search, wpath) - wpath_search[len(wpath)+0] = '\\' - wpath_search[len(wpath)+1] = '*' - wpath_search[len(wpath)+2] = 0 - - path := cleanpath_from_buf(wpath) - defer delete(path) - - find_data := &win32.WIN32_FIND_DATAW{} - find_handle := win32.FindFirstFileW(cstring16(raw_data(wpath_search)), find_data) - if find_handle == win32.INVALID_HANDLE_VALUE { - err = get_last_error() - return dfi[:], err - } - defer win32.FindClose(find_handle) - for n != 0 { - fi: File_Info - fi = find_data_to_file_info(path, find_data) - if fi.name != "" { - append(&dfi, fi) - n -= 1 - } - - if !win32.FindNextFileW(find_handle, find_data) { - e := get_last_error() - if e == ERROR_NO_MORE_FILES { - break - } - return dfi[:], e - } - } - - return dfi[:], nil -} diff --git a/core/os/old/doc.odin b/core/os/old/doc.odin deleted file mode 100644 index 525c0b96d..000000000 --- a/core/os/old/doc.odin +++ /dev/null @@ -1,2 +0,0 @@ -// The original implementation of `core:os`, to be removed in Q2 2026. -package os_old \ No newline at end of file diff --git a/core/os/old/env_windows.odin b/core/os/old/env_windows.odin deleted file mode 100644 index f9480340c..000000000 --- a/core/os/old/env_windows.odin +++ /dev/null @@ -1,140 +0,0 @@ -package os_old - -import win32 "core:sys/windows" -import "base:runtime" - -// lookup_env gets the value of the environment variable named by the key -// If the variable is found in the environment the value (which can be empty) is returned and the boolean is true -// Otherwise the returned value will be empty and the boolean will be false -// NOTE: the value will be allocated with the supplied allocator -@(require_results) -lookup_env_alloc :: proc(key: string, allocator := context.allocator) -> (value: string, found: bool) { - if key == "" { - return - } - wkey := win32.utf8_to_wstring(key) - n := win32.GetEnvironmentVariableW(wkey, nil, 0) - if n == 0 && get_last_error() == ERROR_ENVVAR_NOT_FOUND { - return "", false - } - runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD(ignore = context.temp_allocator == allocator) - - b, _ := make([dynamic]u16, n, context.temp_allocator) - n = win32.GetEnvironmentVariableW(wkey, raw_data(b), u32(len(b))) - if n == 0 && get_last_error() == ERROR_ENVVAR_NOT_FOUND { - return "", false - } - value, _ = win32.utf16_to_utf8(b[:n], allocator) - found = true - return -} - -// This version of `lookup_env` doesn't allocate and instead requires the user to provide a buffer. -// Note that it is limited to environment names and values of 512 utf-16 values each -// due to the necessary utf-8 <> utf-16 conversion. -@(require_results) -lookup_env_buffer :: proc(buf: []u8, key: string) -> (value: string, err: Error) { - key_buf: [513]u16 - wkey := win32.utf8_to_wstring(key_buf[:], key) - if wkey == nil { - return "", .Buffer_Full - } - - n2 := win32.GetEnvironmentVariableW(wkey, nil, 0) - if n2 == 0 { - return "", .Env_Var_Not_Found - } - - val_buf: [513]u16 - n2 = win32.GetEnvironmentVariableW(wkey, raw_data(val_buf[:]), u32(len(val_buf[:]))) - if n2 == 0 { - return "", .Env_Var_Not_Found - } else if int(n2) > len(buf) { - return "", .Buffer_Full - } - - value = win32.utf16_to_utf8(buf, val_buf[:n2]) - - return value, nil -} -lookup_env :: proc{lookup_env_alloc, lookup_env_buffer} - -// get_env retrieves the value of the environment variable named by the key -// It returns the value, which will be empty if the variable is not present -// To distinguish between an empty value and an unset value, use lookup_env -// NOTE: the value will be allocated with the supplied allocator -@(require_results) -get_env_alloc :: proc(key: string, allocator := context.allocator) -> (value: string) { - value, _ = lookup_env(key, allocator) - return -} - -@(require_results) -get_env_buf :: proc(buf: []u8, key: string) -> (value: string) { - value, _ = lookup_env(buf, key) - return -} -get_env :: proc{get_env_alloc, get_env_buf} - - -// set_env sets the value of the environment variable named by the key -set_env :: proc(key, value: string) -> Error { - k := win32.utf8_to_wstring(key) - v := win32.utf8_to_wstring(value) - - if !win32.SetEnvironmentVariableW(k, v) { - return get_last_error() - } - return nil -} - -// unset_env unsets a single environment variable -unset_env :: proc(key: string) -> Error { - k := win32.utf8_to_wstring(key) - if !win32.SetEnvironmentVariableW(k, nil) { - return get_last_error() - } - return nil -} - -// environ returns a copy of strings representing the environment, in the form "key=value" -// NOTE: the slice of strings and the strings with be allocated using the supplied allocator -@(require_results) -environ :: proc(allocator := context.allocator) -> []string { - envs := ([^]win32.WCHAR)(win32.GetEnvironmentStringsW()) - if envs == nil { - return nil - } - defer win32.FreeEnvironmentStringsW(envs) - - r, err := make([dynamic]string, 0, 50, allocator) - if err != nil { - return nil - } - for from, i := 0, 0; true; i += 1 { - if c := envs[i]; c == 0 { - if i <= from { - break - } - append(&r, win32.utf16_to_utf8(envs[from:i], allocator) or_else "") - from = i + 1 - } - } - - return r[:] -} - - -// clear_env deletes all environment variables -clear_env :: proc() { - runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD() - envs := environ(context.temp_allocator) - for env in envs { - for j in 1.. string where intrinsics.type_is_enum(Platform_Error) { - if e == nil { - return "" - } - - when ODIN_OS == .Darwin { - if s := string(_darwin_string_error(i32(e))); s != "" { - return s - } - } - - when ODIN_OS != .Linux { - @(require_results) - binary_search :: proc "contextless" (array: $A/[]$T, key: T) -> (index: int, found: bool) #no_bounds_check { - n := len(array) - left, right := 0, n - for left < right { - mid := int(uint(left+right) >> 1) - if array[mid] < key { - left = mid+1 - } else { - // equal or greater - right = mid - } - } - return left, left < n && array[left] == key - } - - err := runtime.Type_Info_Enum_Value(e) - - ti := &runtime.type_info_base(type_info_of(Platform_Error)).variant.(runtime.Type_Info_Enum) - if idx, ok := binary_search(ti.values, err); ok { - return ti.names[idx] - } - } else { - @(rodata, static) - pe_strings := [Platform_Error]string{ - .NONE = "", - .EPERM = "Operation not permitted", - .ENOENT = "No such file or directory", - .ESRCH = "No such process", - .EINTR = "Interrupted system call", - .EIO = "Input/output error", - .ENXIO = "No such device or address", - .E2BIG = "Argument list too long", - .ENOEXEC = "Exec format error", - .EBADF = "Bad file descriptor", - .ECHILD = "No child processes", - .EAGAIN = "Resource temporarily unavailable", - .ENOMEM = "Cannot allocate memory", - .EACCES = "Permission denied", - .EFAULT = "Bad address", - .ENOTBLK = "Block device required", - .EBUSY = "Device or resource busy", - .EEXIST = "File exists", - .EXDEV = "Invalid cross-device link", - .ENODEV = "No such device", - .ENOTDIR = "Not a directory", - .EISDIR = "Is a directory", - .EINVAL = "Invalid argument", - .ENFILE = "Too many open files in system", - .EMFILE = "Too many open files", - .ENOTTY = "Inappropriate ioctl for device", - .ETXTBSY = "Text file busy", - .EFBIG = "File too large", - .ENOSPC = "No space left on device", - .ESPIPE = "Illegal seek", - .EROFS = "Read-only file system", - .EMLINK = "Too many links", - .EPIPE = "Broken pipe", - .EDOM = "Numerical argument out of domain", - .ERANGE = "Numerical result out of range", - .EDEADLK = "Resource deadlock avoided", - .ENAMETOOLONG = "File name too long", - .ENOLCK = "No locks available", - .ENOSYS = "Function not implemented", - .ENOTEMPTY = "Directory not empty", - .ELOOP = "Too many levels of symbolic links", - .EUNKNOWN_41 = "Unknown Error (41)", - .ENOMSG = "No message of desired type", - .EIDRM = "Identifier removed", - .ECHRNG = "Channel number out of range", - .EL2NSYNC = "Level 2 not synchronized", - .EL3HLT = "Level 3 halted", - .EL3RST = "Level 3 reset", - .ELNRNG = "Link number out of range", - .EUNATCH = "Protocol driver not attached", - .ENOCSI = "No CSI structure available", - .EL2HLT = "Level 2 halted", - .EBADE = "Invalid exchange", - .EBADR = "Invalid request descriptor", - .EXFULL = "Exchange full", - .ENOANO = "No anode", - .EBADRQC = "Invalid request code", - .EBADSLT = "Invalid slot", - .EUNKNOWN_58 = "Unknown Error (58)", - .EBFONT = "Bad font file format", - .ENOSTR = "Device not a stream", - .ENODATA = "No data available", - .ETIME = "Timer expired", - .ENOSR = "Out of streams resources", - .ENONET = "Machine is not on the network", - .ENOPKG = "Package not installed", - .EREMOTE = "Object is remote", - .ENOLINK = "Link has been severed", - .EADV = "Advertise error", - .ESRMNT = "Srmount error", - .ECOMM = "Communication error on send", - .EPROTO = "Protocol error", - .EMULTIHOP = "Multihop attempted", - .EDOTDOT = "RFS specific error", - .EBADMSG = "Bad message", - .EOVERFLOW = "Value too large for defined data type", - .ENOTUNIQ = "Name not unique on network", - .EBADFD = "File descriptor in bad state", - .EREMCHG = "Remote address changed", - .ELIBACC = "Can not access a needed shared library", - .ELIBBAD = "Accessing a corrupted shared library", - .ELIBSCN = ".lib section in a.out corrupted", - .ELIBMAX = "Attempting to link in too many shared libraries", - .ELIBEXEC = "Cannot exec a shared library directly", - .EILSEQ = "Invalid or incomplete multibyte or wide character", - .ERESTART = "Interrupted system call should be restarted", - .ESTRPIPE = "Streams pipe error", - .EUSERS = "Too many users", - .ENOTSOCK = "Socket operation on non-socket", - .EDESTADDRREQ = "Destination address required", - .EMSGSIZE = "Message too long", - .EPROTOTYPE = "Protocol wrong type for socket", - .ENOPROTOOPT = "Protocol not available", - .EPROTONOSUPPORT = "Protocol not supported", - .ESOCKTNOSUPPORT = "Socket type not supported", - .EOPNOTSUPP = "Operation not supported", - .EPFNOSUPPORT = "Protocol family not supported", - .EAFNOSUPPORT = "Address family not supported by protocol", - .EADDRINUSE = "Address already in use", - .EADDRNOTAVAIL = "Cannot assign requested address", - .ENETDOWN = "Network is down", - .ENETUNREACH = "Network is unreachable", - .ENETRESET = "Network dropped connection on reset", - .ECONNABORTED = "Software caused connection abort", - .ECONNRESET = "Connection reset by peer", - .ENOBUFS = "No buffer space available", - .EISCONN = "Transport endpoint is already connected", - .ENOTCONN = "Transport endpoint is not connected", - .ESHUTDOWN = "Cannot send after transport endpoint shutdown", - .ETOOMANYREFS = "Too many references: cannot splice", - .ETIMEDOUT = "Connection timed out", - .ECONNREFUSED = "Connection refused", - .EHOSTDOWN = "Host is down", - .EHOSTUNREACH = "No route to host", - .EALREADY = "Operation already in progress", - .EINPROGRESS = "Operation now in progress", - .ESTALE = "Stale file handle", - .EUCLEAN = "Structure needs cleaning", - .ENOTNAM = "Not a XENIX named type file", - .ENAVAIL = "No XENIX semaphores available", - .EISNAM = "Is a named type file", - .EREMOTEIO = "Remote I/O error", - .EDQUOT = "Disk quota exceeded", - .ENOMEDIUM = "No medium found", - .EMEDIUMTYPE = "Wrong medium type", - .ECANCELED = "Operation canceled", - .ENOKEY = "Required key not available", - .EKEYEXPIRED = "Key has expired", - .EKEYREVOKED = "Key has been revoked", - .EKEYREJECTED = "Key was rejected by service", - .EOWNERDEAD = "Owner died", - .ENOTRECOVERABLE = "State not recoverable", - .ERFKILL = "Operation not possible due to RF-kill", - .EHWPOISON = "Memory page has hardware error", - } - if Platform_Error.NONE <= e && e <= max(Platform_Error) { - return pe_strings[e] - } - } - return "" -} - -@(private, require_results) -error_to_io_error :: proc(ferr: Error) -> io.Error { - if ferr == nil { - return .None - } - return ferr.(io.Error) or_else .Unknown -} diff --git a/core/os/old/os.odin b/core/os/old/os.odin deleted file mode 100644 index 01e93126d..000000000 --- a/core/os/old/os.odin +++ /dev/null @@ -1,266 +0,0 @@ -// Cross-platform `OS` interactions like file `I/O`. -package os_old - -import "base:intrinsics" -import "base:runtime" -import "core:io" -import "core:strconv" -import "core:strings" -import "core:unicode/utf8" - - -OS :: ODIN_OS -ARCH :: ODIN_ARCH -ENDIAN :: ODIN_ENDIAN - -SEEK_SET :: 0 -SEEK_CUR :: 1 -SEEK_END :: 2 - -write_string :: proc(fd: Handle, str: string) -> (int, Error) { - return write(fd, transmute([]byte)str) -} - -write_byte :: proc(fd: Handle, b: byte) -> (int, Error) { - return write(fd, []byte{b}) -} - -write_rune :: proc(fd: Handle, r: rune) -> (int, Error) { - if r < utf8.RUNE_SELF { - return write_byte(fd, byte(r)) - } - - b, n := utf8.encode_rune(r) - return write(fd, b[:n]) -} - -write_encoded_rune :: proc(f: Handle, r: rune) -> (n: int, err: Error) { - wrap :: proc(m: int, merr: Error, n: ^int, err: ^Error) -> bool { - n^ += m - if merr != nil { - err^ = merr - return true - } - return false - } - - if wrap(write_byte(f, '\''), &n, &err) { return } - - switch r { - case '\a': if wrap(write_string(f, "\\a"), &n, &err) { return } - case '\b': if wrap(write_string(f, "\\b"), &n, &err) { return } - case '\e': if wrap(write_string(f, "\\e"), &n, &err) { return } - case '\f': if wrap(write_string(f, "\\f"), &n, &err) { return } - case '\n': if wrap(write_string(f, "\\n"), &n, &err) { return } - case '\r': if wrap(write_string(f, "\\r"), &n, &err) { return } - case '\t': if wrap(write_string(f, "\\t"), &n, &err) { return } - case '\v': if wrap(write_string(f, "\\v"), &n, &err) { return } - case: - if r < 32 { - if wrap(write_string(f, "\\x"), &n, &err) { return } - b: [2]byte - s := strconv.write_bits(b[:], u64(r), 16, true, 64, strconv.digits, nil) - switch len(s) { - case 0: if wrap(write_string(f, "00"), &n, &err) { return } - case 1: if wrap(write_rune(f, '0'), &n, &err) { return } - case 2: if wrap(write_string(f, s), &n, &err) { return } - } - } else { - if wrap(write_rune(f, r), &n, &err) { return } - } - } - _ = wrap(write_byte(f, '\''), &n, &err) - return -} - -read_at_least :: proc(fd: Handle, buf: []byte, min: int) -> (n: int, err: Error) { - if len(buf) < min { - return 0, io.Error.Short_Buffer - } - nn := max(int) - for nn > 0 && n < min && err == nil { - nn, err = read(fd, buf[n:]) - n += nn - } - if n >= min { - err = nil - } - return -} - -read_full :: proc(fd: Handle, buf: []byte) -> (n: int, err: Error) { - return read_at_least(fd, buf, len(buf)) -} - - -@(require_results) -file_size_from_path :: proc(path: string) -> i64 { - fd, err := open(path, O_RDONLY, 0) - if err != nil { - return -1 - } - defer close(fd) - - length: i64 - if length, err = file_size(fd); err != nil { - return -1 - } - return length -} - -@(require_results) -read_entire_file_from_filename :: proc(name: string, allocator := context.allocator, loc := #caller_location) -> (data: []byte, success: bool) { - err: Error - data, err = read_entire_file_from_filename_or_err(name, allocator, loc) - success = err == nil - return -} - -@(require_results) -read_entire_file_from_handle :: proc(fd: Handle, allocator := context.allocator, loc := #caller_location) -> (data: []byte, success: bool) { - err: Error - data, err = read_entire_file_from_handle_or_err(fd, allocator, loc) - success = err == nil - return -} - -read_entire_file :: proc { - read_entire_file_from_filename, - read_entire_file_from_handle, -} - -@(require_results) -read_entire_file_from_filename_or_err :: proc(name: string, allocator := context.allocator, loc := #caller_location) -> (data: []byte, err: Error) { - context.allocator = allocator - - fd := open(name, O_RDONLY, 0) or_return - defer close(fd) - - return read_entire_file_from_handle_or_err(fd, allocator, loc) -} - -@(require_results) -read_entire_file_from_handle_or_err :: proc(fd: Handle, allocator := context.allocator, loc := #caller_location) -> (data: []byte, err: Error) { - context.allocator = allocator - - length := file_size(fd) or_return - if length <= 0 { - return nil, nil - } - - data = make([]byte, int(length), allocator, loc) or_return - if data == nil { - return nil, nil - } - defer if err != nil { - delete(data, allocator) - } - - bytes_read := read_full(fd, data) or_return - data = data[:bytes_read] - return -} - -read_entire_file_or_err :: proc { - read_entire_file_from_filename_or_err, - read_entire_file_from_handle_or_err, -} - - -write_entire_file :: proc(name: string, data: []byte, truncate := true) -> (success: bool) { - return write_entire_file_or_err(name, data, truncate) == nil -} - -@(require_results) -write_entire_file_or_err :: proc(name: string, data: []byte, truncate := true) -> Error { - flags: int = O_WRONLY|O_CREATE - if truncate { - flags |= O_TRUNC - } - - mode: int = 0 - when OS == .Linux || OS == .Darwin { - // NOTE(justasd): 644 (owner read, write; group read; others read) - mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH - } - - fd := open(name, flags, mode) or_return - defer close(fd) - - for n := 0; n < len(data); { - n += write(fd, data[n:]) or_return - } - return nil -} - -write_ptr :: proc(fd: Handle, data: rawptr, len: int) -> (int, Error) { - return write(fd, ([^]byte)(data)[:len]) -} - -read_ptr :: proc(fd: Handle, data: rawptr, len: int) -> (int, Error) { - return read(fd, ([^]byte)(data)[:len]) -} - -heap_allocator_proc :: runtime.heap_allocator_proc -heap_allocator :: runtime.heap_allocator - -heap_alloc :: runtime.heap_alloc -heap_resize :: runtime.heap_resize -heap_free :: runtime.heap_free - -@(require_results) -processor_core_count :: proc() -> int { - return _processor_core_count() -} - -// Always allocates for consistency. -replace_environment_placeholders :: proc(path: string, allocator := context.allocator) -> (res: string) { - path := path - - sb: strings.Builder - strings.builder_init_none(&sb, allocator) - for len(path) > 0 { - switch path[0] { - case '%': // Windows - when ODIN_OS == .Windows { - for r, i in path[1:] { - if r == '%' { - env_key := path[1:i+1] - env_val := get_env(env_key, context.temp_allocator) - strings.write_string(&sb, env_val) - path = path[i+1:] // % is part of key, so skip 1 character extra - } - } - } else { - strings.write_rune(&sb, rune(path[0])) - } - - case '$': // Posix - when ODIN_OS != .Windows { - env_key := "" - dollar_loop: for r, i in path[1:] { - switch r { - case 'A'..='Z', 'a'..='z', '0'..='9', '_': // Part of key ident - case: - env_key = path[1:i+1] - break dollar_loop - } - } - if len(env_key) > 0 { - env_val := get_env(env_key, context.temp_allocator) - strings.write_string(&sb, env_val) - path = path[len(env_key):] - } - - } else { - strings.write_rune(&sb, rune(path[0])) - } - - case: - strings.write_rune(&sb, rune(path[0])) - } - - path = path[1:] - } - return strings.to_string(sb) -} \ No newline at end of file diff --git a/core/os/old/os_darwin.odin b/core/os/old/os_darwin.odin deleted file mode 100644 index 6a6efe4e2..000000000 --- a/core/os/old/os_darwin.odin +++ /dev/null @@ -1,1348 +0,0 @@ -package os_old - -foreign import dl "system:dl" -foreign import libc "system:System" -foreign import pthread "system:System" - -import "base:runtime" -import "core:strings" -import "core:c" - -Handle :: distinct i32 -File_Time :: distinct u64 - -INVALID_HANDLE :: ~Handle(0) - -_Platform_Error :: enum i32 { - NONE = 0, - EPERM = 1, /* Operation not permitted */ - ENOENT = 2, /* No such file or directory */ - ESRCH = 3, /* No such process */ - EINTR = 4, /* Interrupted system call */ - EIO = 5, /* Input/output error */ - ENXIO = 6, /* Device not configured */ - E2BIG = 7, /* Argument list too long */ - ENOEXEC = 8, /* Exec format error */ - EBADF = 9, /* Bad file descriptor */ - ECHILD = 10, /* No child processes */ - EDEADLK = 11, /* Resource deadlock avoided */ - ENOMEM = 12, /* Cannot allocate memory */ - EACCES = 13, /* Permission denied */ - EFAULT = 14, /* Bad address */ - ENOTBLK = 15, /* Block device required */ - EBUSY = 16, /* Device / Resource busy */ - EEXIST = 17, /* File exists */ - EXDEV = 18, /* Cross-device link */ - ENODEV = 19, /* Operation not supported by device */ - ENOTDIR = 20, /* Not a directory */ - EISDIR = 21, /* Is a directory */ - EINVAL = 22, /* Invalid argument */ - ENFILE = 23, /* Too many open files in system */ - EMFILE = 24, /* Too many open files */ - ENOTTY = 25, /* Inappropriate ioctl for device */ - ETXTBSY = 26, /* Text file busy */ - EFBIG = 27, /* File too large */ - ENOSPC = 28, /* No space left on device */ - ESPIPE = 29, /* Illegal seek */ - EROFS = 30, /* Read-only file system */ - EMLINK = 31, /* Too many links */ - EPIPE = 32, /* Broken pipe */ - - /* math software */ - EDOM = 33, /* Numerical argument out of domain */ - ERANGE = 34, /* Result too large */ - - /* non-blocking and interrupt i/o */ - EAGAIN = 35, /* Resource temporarily unavailable */ - EWOULDBLOCK = EAGAIN, /* Operation would block */ - EINPROGRESS = 36, /* Operation now in progress */ - EALREADY = 37, /* Operation already in progress */ - - /* ipc/network software -- argument errors */ - ENOTSOCK = 38, /* Socket operation on non-socket */ - EDESTADDRREQ = 39, /* Destination address required */ - EMSGSIZE = 40, /* Message too long */ - EPROTOTYPE = 41, /* Protocol wrong type for socket */ - ENOPROTOOPT = 42, /* Protocol not available */ - EPROTONOSUPPORT = 43, /* Protocol not supported */ - ESOCKTNOSUPPORT = 44, /* Socket type not supported */ - ENOTSUP = 45, /* Operation not supported */ - EOPNOTSUPP = ENOTSUP, - EPFNOSUPPORT = 46, /* Protocol family not supported */ - EAFNOSUPPORT = 47, /* Address family not supported by protocol family */ - EADDRINUSE = 48, /* Address already in use */ - EADDRNOTAVAIL = 49, /* Can't assign requested address */ - - /* ipc/network software -- operational errors */ - ENETDOWN = 50, /* Network is down */ - ENETUNREACH = 51, /* Network is unreachable */ - ENETRESET = 52, /* Network dropped connection on reset */ - ECONNABORTED = 53, /* Software caused connection abort */ - ECONNRESET = 54, /* Connection reset by peer */ - ENOBUFS = 55, /* No buffer space available */ - EISCONN = 56, /* Socket is already connected */ - ENOTCONN = 57, /* Socket is not connected */ - ESHUTDOWN = 58, /* Can't send after socket shutdown */ - ETOOMANYREFS = 59, /* Too many references: can't splice */ - ETIMEDOUT = 60, /* Operation timed out */ - ECONNREFUSED = 61, /* Connection refused */ - - ELOOP = 62, /* Too many levels of symbolic links */ - ENAMETOOLONG = 63, /* File name too long */ - - /* should be rearranged */ - EHOSTDOWN = 64, /* Host is down */ - EHOSTUNREACH = 65, /* No route to host */ - ENOTEMPTY = 66, /* Directory not empty */ - - /* quotas & mush */ - EPROCLIM = 67, /* Too many processes */ - EUSERS = 68, /* Too many users */ - EDQUOT = 69, /* Disc quota exceeded */ - - /* Network File System */ - ESTALE = 70, /* Stale NFS file handle */ - EREMOTE = 71, /* Too many levels of remote in path */ - EBADRPC = 72, /* RPC struct is bad */ - ERPCMISMATCH = 73, /* RPC version wrong */ - EPROGUNAVAIL = 74, /* RPC prog. not avail */ - EPROGMISMATCH = 75, /* Program version wrong */ - EPROCUNAVAIL = 76, /* Bad procedure for program */ - - ENOLCK = 77, /* No locks available */ - ENOSYS = 78, /* Function not implemented */ - - EFTYPE = 79, /* Inappropriate file type or format */ - EAUTH = 80, /* Authentication error */ - ENEEDAUTH = 81, /* Need authenticator */ - - /* Intelligent device errors */ - EPWROFF = 82, /* Device power is off */ - EDEVERR = 83, /* Device error, e.g. paper out */ - EOVERFLOW = 84, /* Value too large to be stored in data type */ - - /* Program loading errors */ - EBADEXEC = 85, /* Bad executable */ - EBADARCH = 86, /* Bad CPU type in executable */ - ESHLIBVERS = 87, /* Shared library version mismatch */ - EBADMACHO = 88, /* Malformed Macho file */ - - ECANCELED = 89, /* Operation canceled */ - - EIDRM = 90, /* Identifier removed */ - ENOMSG = 91, /* No message of desired type */ - EILSEQ = 92, /* Illegal byte sequence */ - ENOATTR = 93, /* Attribute not found */ - - EBADMSG = 94, /* Bad message */ - EMULTIHOP = 95, /* Reserved */ - ENODATA = 96, /* No message available on STREAM */ - ENOLINK = 97, /* Reserved */ - ENOSR = 98, /* No STREAM resources */ - ENOSTR = 99, /* Not a STREAM */ - EPROTO = 100, /* Protocol error */ - ETIME = 101, /* STREAM ioctl timeout */ - - ENOPOLICY = 103, /* No such policy registered */ - - ENOTRECOVERABLE = 104, /* State not recoverable */ - EOWNERDEAD = 105, /* Previous owner died */ - - EQFULL = 106, /* Interface output queue is full */ - ELAST = 106, /* Must be equal largest errno */ -} - -EPERM :: _Platform_Error.EPERM -ENOENT :: _Platform_Error.ENOENT -ESRCH :: _Platform_Error.ESRCH -EINTR :: _Platform_Error.EINTR -EIO :: _Platform_Error.EIO -ENXIO :: _Platform_Error.ENXIO -E2BIG :: _Platform_Error.E2BIG -ENOEXEC :: _Platform_Error.ENOEXEC -EBADF :: _Platform_Error.EBADF -ECHILD :: _Platform_Error.ECHILD -EDEADLK :: _Platform_Error.EDEADLK -ENOMEM :: _Platform_Error.ENOMEM -EACCES :: _Platform_Error.EACCES -EFAULT :: _Platform_Error.EFAULT -ENOTBLK :: _Platform_Error.ENOTBLK -EBUSY :: _Platform_Error.EBUSY -EEXIST :: _Platform_Error.EEXIST -EXDEV :: _Platform_Error.EXDEV -ENODEV :: _Platform_Error.ENODEV -ENOTDIR :: _Platform_Error.ENOTDIR -EISDIR :: _Platform_Error.EISDIR -EINVAL :: _Platform_Error.EINVAL -ENFILE :: _Platform_Error.ENFILE -EMFILE :: _Platform_Error.EMFILE -ENOTTY :: _Platform_Error.ENOTTY -ETXTBSY :: _Platform_Error.ETXTBSY -EFBIG :: _Platform_Error.EFBIG -ENOSPC :: _Platform_Error.ENOSPC -ESPIPE :: _Platform_Error.ESPIPE -EROFS :: _Platform_Error.EROFS -EMLINK :: _Platform_Error.EMLINK -EPIPE :: _Platform_Error.EPIPE - -/* math software */ -EDOM :: _Platform_Error.EDOM -ERANGE :: _Platform_Error.ERANGE - -/* non-blocking and interrupt i/o */ -EAGAIN :: _Platform_Error.EAGAIN -EWOULDBLOCK :: _Platform_Error.EWOULDBLOCK -EINPROGRESS :: _Platform_Error.EINPROGRESS -EALREADY :: _Platform_Error.EALREADY - -/* ipc/network software -- argument errors */ -ENOTSOCK :: _Platform_Error.ENOTSOCK -EDESTADDRREQ :: _Platform_Error.EDESTADDRREQ -EMSGSIZE :: _Platform_Error.EMSGSIZE -EPROTOTYPE :: _Platform_Error.EPROTOTYPE -ENOPROTOOPT :: _Platform_Error.ENOPROTOOPT -EPROTONOSUPPORT :: _Platform_Error.EPROTONOSUPPORT -ESOCKTNOSUPPORT :: _Platform_Error.ESOCKTNOSUPPORT -ENOTSUP :: _Platform_Error.ENOTSUP -EOPNOTSUPP :: _Platform_Error.EOPNOTSUPP -EPFNOSUPPORT :: _Platform_Error.EPFNOSUPPORT -EAFNOSUPPORT :: _Platform_Error.EAFNOSUPPORT -EADDRINUSE :: _Platform_Error.EADDRINUSE -EADDRNOTAVAIL :: _Platform_Error.EADDRNOTAVAIL - -/* ipc/network software -- operational errors */ -ENETDOWN :: _Platform_Error.ENETDOWN -ENETUNREACH :: _Platform_Error.ENETUNREACH -ENETRESET :: _Platform_Error.ENETRESET -ECONNABORTED :: _Platform_Error.ECONNABORTED -ECONNRESET :: _Platform_Error.ECONNRESET -ENOBUFS :: _Platform_Error.ENOBUFS -EISCONN :: _Platform_Error.EISCONN -ENOTCONN :: _Platform_Error.ENOTCONN -ESHUTDOWN :: _Platform_Error.ESHUTDOWN -ETOOMANYREFS :: _Platform_Error.ETOOMANYREFS -ETIMEDOUT :: _Platform_Error.ETIMEDOUT -ECONNREFUSED :: _Platform_Error.ECONNREFUSED - -ELOOP :: _Platform_Error.ELOOP -ENAMETOOLONG :: _Platform_Error.ENAMETOOLONG - -/* should be rearranged */ -EHOSTDOWN :: _Platform_Error.EHOSTDOWN -EHOSTUNREACH :: _Platform_Error.EHOSTUNREACH -ENOTEMPTY :: _Platform_Error.ENOTEMPTY - -/* quotas & mush */ -EPROCLIM :: _Platform_Error.EPROCLIM -EUSERS :: _Platform_Error.EUSERS -EDQUOT :: _Platform_Error.EDQUOT - -/* Network File System */ -ESTALE :: _Platform_Error.ESTALE -EREMOTE :: _Platform_Error.EREMOTE -EBADRPC :: _Platform_Error.EBADRPC -ERPCMISMATCH :: _Platform_Error.ERPCMISMATCH -EPROGUNAVAIL :: _Platform_Error.EPROGUNAVAIL -EPROGMISMATCH :: _Platform_Error.EPROGMISMATCH -EPROCUNAVAIL :: _Platform_Error.EPROCUNAVAIL - -ENOLCK :: _Platform_Error.ENOLCK -ENOSYS :: _Platform_Error.ENOSYS - -EFTYPE :: _Platform_Error.EFTYPE -EAUTH :: _Platform_Error.EAUTH -ENEEDAUTH :: _Platform_Error.ENEEDAUTH - -/* Intelligent device errors */ -EPWROFF :: _Platform_Error.EPWROFF -EDEVERR :: _Platform_Error.EDEVERR -EOVERFLOW :: _Platform_Error.EOVERFLOW - -/* Program loading errors */ -EBADEXEC :: _Platform_Error.EBADEXEC -EBADARCH :: _Platform_Error.EBADARCH -ESHLIBVERS :: _Platform_Error.ESHLIBVERS -EBADMACHO :: _Platform_Error.EBADMACHO - -ECANCELED :: _Platform_Error.ECANCELED - -EIDRM :: _Platform_Error.EIDRM -ENOMSG :: _Platform_Error.ENOMSG -EILSEQ :: _Platform_Error.EILSEQ -ENOATTR :: _Platform_Error.ENOATTR - -EBADMSG :: _Platform_Error.EBADMSG -EMULTIHOP :: _Platform_Error.EMULTIHOP -ENODATA :: _Platform_Error.ENODATA -ENOLINK :: _Platform_Error.ENOLINK -ENOSR :: _Platform_Error.ENOSR -ENOSTR :: _Platform_Error.ENOSTR -EPROTO :: _Platform_Error.EPROTO -ETIME :: _Platform_Error.ETIME - -ENOPOLICY :: _Platform_Error.ENOPOLICY - -ENOTRECOVERABLE :: _Platform_Error.ENOTRECOVERABLE -EOWNERDEAD :: _Platform_Error.EOWNERDEAD - -EQFULL :: _Platform_Error.EQFULL -ELAST :: _Platform_Error.ELAST - - -O_RDONLY :: 0x0000 -O_WRONLY :: 0x0001 -O_RDWR :: 0x0002 -O_CREATE :: 0x0200 -O_EXCL :: 0x0800 -O_NOCTTY :: 0 -O_TRUNC :: 0x0400 -O_NONBLOCK :: 0x0004 -O_APPEND :: 0x0008 -O_SYNC :: 0x0080 -O_ASYNC :: 0x0040 -O_CLOEXEC :: 0x1000000 - -SEEK_DATA :: 3 -SEEK_HOLE :: 4 -SEEK_MAX :: SEEK_HOLE - - - -// NOTE(zangent): These are OS specific! -// Do not mix these up! -RTLD_LAZY :: 0x1 -RTLD_NOW :: 0x2 -RTLD_LOCAL :: 0x4 -RTLD_GLOBAL :: 0x8 -RTLD_NODELETE :: 0x80 -RTLD_NOLOAD :: 0x10 -RTLD_FIRST :: 0x100 - -SOL_SOCKET :: 0xFFFF - -SOCK_STREAM :: 1 -SOCK_DGRAM :: 2 -SOCK_RAW :: 3 -SOCK_RDM :: 4 -SOCK_SEQPACKET :: 5 - -SO_DEBUG :: 0x0001 -SO_ACCEPTCONN :: 0x0002 -SO_REUSEADDR :: 0x0004 -SO_KEEPALIVE :: 0x0008 -SO_DONTROUTE :: 0x0010 -SO_BROADCAST :: 0x0020 -SO_USELOOPBACK :: 0x0040 -SO_LINGER :: 0x0080 -SO_OOBINLINE :: 0x0100 -SO_REUSEPORT :: 0x0200 -SO_TIMESTAMP :: 0x0400 - -SO_DONTTRUNC :: 0x2000 -SO_WANTMORE :: 0x4000 -SO_WANTOOBFLAG :: 0x8000 -SO_SNDBUF :: 0x1001 -SO_RCVBUF :: 0x1002 -SO_SNDLOWAT :: 0x1003 -SO_RCVLOWAT :: 0x1004 -SO_SNDTIMEO :: 0x1005 -SO_RCVTIMEO :: 0x1006 -SO_ERROR :: 0x1007 -SO_TYPE :: 0x1008 -SO_PRIVSTATE :: 0x1009 -SO_NREAD :: 0x1020 -SO_NKE :: 0x1021 - -AF_UNSPEC :: 0 -AF_LOCAL :: 1 -AF_UNIX :: AF_LOCAL -AF_INET :: 2 -AF_IMPLINK :: 3 -AF_PUP :: 4 -AF_CHAOS :: 5 -AF_NS :: 6 -AF_ISO :: 7 -AF_OSI :: AF_ISO -AF_ECMA :: 8 -AF_DATAKIT :: 9 -AF_CCITT :: 10 -AF_SNA :: 11 -AF_DECnet :: 12 -AF_DLI :: 13 -AF_LAT :: 14 -AF_HYLINK :: 15 -AF_APPLETALK :: 16 -AF_ROUTE :: 17 -AF_LINK :: 18 -pseudo_AF_XTP :: 19 -AF_COIP :: 20 -AF_CNT :: 21 -pseudo_AF_RTIP :: 22 -AF_IPX :: 23 -AF_SIP :: 24 -pseudo_AF_PIP :: 25 -pseudo_AF_BLUE :: 26 -AF_NDRV :: 27 -AF_ISDN :: 28 -AF_E164 :: AF_ISDN -pseudo_AF_KEY :: 29 -AF_INET6 :: 30 -AF_NATM :: 31 -AF_SYSTEM :: 32 -AF_NETBIOS :: 33 -AF_PPP :: 34 - -TCP_NODELAY :: 0x01 -TCP_MAXSEG :: 0x02 -TCP_NOPUSH :: 0x04 -TCP_NOOPT :: 0x08 - -IPPROTO_ICMP :: 1 -IPPROTO_TCP :: 6 -IPPROTO_UDP :: 17 - -SHUT_RD :: 0 -SHUT_WR :: 1 -SHUT_RDWR :: 2 - -F_GETFL: int : 3 /* Get file flags */ -F_SETFL: int : 4 /* Set file flags */ - -// "Argv" arguments converted to Odin strings -args := _alloc_command_line_arguments() - -Unix_File_Time :: struct { - seconds: i64, - nanoseconds: i64, -} - -OS_Stat :: struct { - device_id: i32, // ID of device containing file - mode: u16, // Mode of the file - nlink: u16, // Number of hard links - serial: u64, // File serial number - uid: u32, // User ID of the file's owner - gid: u32, // Group ID of the file's group - rdev: i32, // Device ID, if device - - last_access: Unix_File_Time, // Time of last access - modified: Unix_File_Time, // Time of last modification - status_change: Unix_File_Time, // Time of last status change - created: Unix_File_Time, // Time of creation - - size: i64, // Size of the file, in bytes - blocks: i64, // Number of blocks allocated for the file - block_size: i32, // Optimal blocksize for I/O - flags: u32, // User-defined flags for the file - gen_num: u32, // File generation number ..? - _spare: i32, // RESERVED - _reserve1, - _reserve2: i64, // RESERVED -} - -DARWIN_MAXPATHLEN :: 1024 -Dirent :: struct { - ino: u64, - off: u64, - reclen: u16, - namlen: u16, - type: u8, - name: [DARWIN_MAXPATHLEN]byte, -} - -Dir :: distinct rawptr // DIR* - -ADDRESS_FAMILY :: c.char -SOCKADDR :: struct #packed { - len: c.char, - family: ADDRESS_FAMILY, - sa_data: [14]c.char, -} - -SOCKADDR_STORAGE_LH :: struct #packed { - len: c.char, - family: ADDRESS_FAMILY, - __ss_pad1: [6]c.char, - __ss_align: i64, - __ss_pad2: [112]c.char, -} - -sockaddr_in :: struct #packed { - sin_len: c.char, - sin_family: ADDRESS_FAMILY, - sin_port: u16be, - sin_addr: in_addr, - sin_zero: [8]c.char, -} - -sockaddr_in6 :: struct #packed { - sin6_len: c.char, - sin6_family: ADDRESS_FAMILY, - sin6_port: u16be, - sin6_flowinfo: c.uint, - sin6_addr: in6_addr, - sin6_scope_id: c.uint, -} - -in_addr :: struct #packed { - s_addr: u32, -} - -in6_addr :: struct #packed { - s6_addr: [16]u8, -} - -// https://github.com/apple/darwin-xnu/blob/2ff845c2e033bd0ff64b5b6aa6063a1f8f65aa32/bsd/sys/socket.h#L1025-L1027 -// Prevent the raising of SIGPIPE on writing to a closed network socket. -MSG_NOSIGNAL :: 0x80000 - -SIOCGIFFLAG :: enum c.int { - UP = 0, /* Interface is up. */ - BROADCAST = 1, /* Broadcast address valid. */ - DEBUG = 2, /* Turn on debugging. */ - LOOPBACK = 3, /* Is a loopback net. */ - POINT_TO_POINT = 4, /* Interface is point-to-point link. */ - NO_TRAILERS = 5, /* Avoid use of trailers. */ - RUNNING = 6, /* Resources allocated. */ - NOARP = 7, /* No address resolution protocol. */ - PROMISC = 8, /* Receive all packets. */ - ALL_MULTI = 9, /* Receive all multicast packets. Unimplemented. */ -} -SIOCGIFFLAGS :: bit_set[SIOCGIFFLAG; c.int] - -ifaddrs :: struct { - next: ^ifaddrs, - name: cstring, - flags: SIOCGIFFLAGS, - address: ^SOCKADDR, - netmask: ^SOCKADDR, - broadcast_or_dest: ^SOCKADDR, // Broadcast or Point-to-Point address - data: rawptr, // Address-specific data. -} - -Timeval :: struct { - seconds: i64, - microseconds: int, -} - -Linger :: struct { - onoff: int, - linger: int, -} - -Socket :: distinct int -socklen_t :: c.int - -// File type -S_IFMT :: 0o170000 // Type of file mask -S_IFIFO :: 0o010000 // Named pipe (fifo) -S_IFCHR :: 0o020000 // Character special -S_IFDIR :: 0o040000 // Directory -S_IFBLK :: 0o060000 // Block special -S_IFREG :: 0o100000 // Regular -S_IFLNK :: 0o120000 // Symbolic link -S_IFSOCK :: 0o140000 // Socket - -// File mode -// Read, write, execute/search by owner -S_IRWXU :: 0o0700 // RWX mask for owner -S_IRUSR :: 0o0400 // R for owner -S_IWUSR :: 0o0200 // W for owner -S_IXUSR :: 0o0100 // X for owner - -// Read, write, execute/search by group -S_IRWXG :: 0o0070 // RWX mask for group -S_IRGRP :: 0o0040 // R for group -S_IWGRP :: 0o0020 // W for group -S_IXGRP :: 0o0010 // X for group - -// Read, write, execute/search by others -S_IRWXO :: 0o0007 // RWX mask for other -S_IROTH :: 0o0004 // R for other -S_IWOTH :: 0o0002 // W for other -S_IXOTH :: 0o0001 // X for other - -S_ISUID :: 0o4000 // Set user id on execution -S_ISGID :: 0o2000 // Set group id on execution -S_ISVTX :: 0o1000 // Directory restrcted delete - -@(require_results) S_ISLNK :: #force_inline proc(m: u16) -> bool { return (m & S_IFMT) == S_IFLNK } -@(require_results) S_ISREG :: #force_inline proc(m: u16) -> bool { return (m & S_IFMT) == S_IFREG } -@(require_results) S_ISDIR :: #force_inline proc(m: u16) -> bool { return (m & S_IFMT) == S_IFDIR } -@(require_results) S_ISCHR :: #force_inline proc(m: u16) -> bool { return (m & S_IFMT) == S_IFCHR } -@(require_results) S_ISBLK :: #force_inline proc(m: u16) -> bool { return (m & S_IFMT) == S_IFBLK } -@(require_results) S_ISFIFO :: #force_inline proc(m: u16) -> bool { return (m & S_IFMT) == S_IFIFO } -@(require_results) S_ISSOCK :: #force_inline proc(m: u16) -> bool { return (m & S_IFMT) == S_IFSOCK } - -R_OK :: 4 // Test for read permission -W_OK :: 2 // Test for write permission -X_OK :: 1 // Test for execute permission -F_OK :: 0 // Test for file existance - -F_GETPATH :: 50 // return the full path of the fd - -foreign libc { - @(link_name="__error") __error :: proc() -> ^c.int --- - - @(link_name="open") _unix_open :: proc(path: cstring, flags: i32, #c_vararg mode: ..u16) -> Handle --- - @(link_name="close") _unix_close :: proc(handle: Handle) -> c.int --- - @(link_name="read") _unix_read :: proc(handle: Handle, buffer: rawptr, count: c.size_t) -> int --- - @(link_name="write") _unix_write :: proc(handle: Handle, buffer: rawptr, count: c.size_t) -> int --- - @(link_name="pread") _unix_pread :: proc(handle: Handle, buffer: rawptr, count: c.size_t, offset: i64) -> int --- - @(link_name="pwrite") _unix_pwrite :: proc(handle: Handle, buffer: rawptr, count: c.size_t, offset: i64) -> int --- - @(link_name="lseek") _unix_lseek :: proc(fs: Handle, offset: int, whence: c.int) -> int --- - @(link_name="gettid") _unix_gettid :: proc() -> u64 --- - @(link_name="getpagesize") _unix_getpagesize :: proc() -> i32 --- - @(link_name="stat64") _unix_stat :: proc(path: cstring, stat: ^OS_Stat) -> c.int --- - @(link_name="lstat64") _unix_lstat :: proc(path: cstring, stat: ^OS_Stat) -> c.int --- - @(link_name="fstat64") _unix_fstat :: proc(fd: Handle, stat: ^OS_Stat) -> c.int --- - @(link_name="readlink") _unix_readlink :: proc(path: cstring, buf: ^byte, bufsiz: c.size_t) -> c.ssize_t --- - @(link_name="access") _unix_access :: proc(path: cstring, mask: c.int) -> c.int --- - @(link_name="fsync") _unix_fsync :: proc(handle: Handle) -> c.int --- - @(link_name="dup") _unix_dup :: proc(handle: Handle) -> Handle --- - - @(link_name="fdopendir$INODE64") _unix_fdopendir_amd64 :: proc(fd: Handle) -> Dir --- - @(link_name="readdir_r$INODE64") _unix_readdir_r_amd64 :: proc(dirp: Dir, entry: ^Dirent, result: ^^Dirent) -> c.int --- - @(link_name="fdopendir") _unix_fdopendir_arm64 :: proc(fd: Handle) -> Dir --- - @(link_name="readdir_r") _unix_readdir_r_arm64 :: proc(dirp: Dir, entry: ^Dirent, result: ^^Dirent) -> c.int --- - - @(link_name="closedir") _unix_closedir :: proc(dirp: Dir) -> c.int --- - @(link_name="rewinddir") _unix_rewinddir :: proc(dirp: Dir) --- - - @(link_name="__fcntl") _unix__fcntl :: proc(fd: Handle, cmd: c.int, arg: uintptr) -> c.int --- - - @(link_name="rename") _unix_rename :: proc(old: cstring, new: cstring) -> c.int --- - @(link_name="remove") _unix_remove :: proc(path: cstring) -> c.int --- - - @(link_name="fchmod") _unix_fchmod :: proc(fd: Handle, mode: u16) -> c.int --- - - @(link_name="malloc") _unix_malloc :: proc(size: int) -> rawptr --- - @(link_name="calloc") _unix_calloc :: proc(num, size: int) -> rawptr --- - @(link_name="free") _unix_free :: proc(ptr: rawptr) --- - @(link_name="realloc") _unix_realloc :: proc(ptr: rawptr, size: int) -> rawptr --- - - @(link_name="getenv") _unix_getenv :: proc(cstring) -> cstring --- - @(link_name="unsetenv") _unix_unsetenv :: proc(cstring) -> c.int --- - @(link_name="setenv") _unix_setenv :: proc(key: cstring, value: cstring, overwrite: c.int) -> c.int --- - - @(link_name="getcwd") _unix_getcwd :: proc(buf: cstring, len: c.size_t) -> cstring --- - @(link_name="chdir") _unix_chdir :: proc(buf: cstring) -> c.int --- - @(link_name="mkdir") _unix_mkdir :: proc(buf: cstring, mode: u16) -> c.int --- - @(link_name="realpath") _unix_realpath :: proc(path: cstring, resolved_path: [^]byte = nil) -> cstring --- - - @(link_name="strerror") _darwin_string_error :: proc(num : c.int) -> cstring --- - @(link_name="sysctlbyname") _sysctlbyname :: proc(path: cstring, oldp: rawptr, oldlenp: rawptr, newp: rawptr, newlen: int) -> c.int --- - - @(link_name="socket") _unix_socket :: proc(domain: c.int, type: c.int, protocol: c.int) -> c.int --- - @(link_name="listen") _unix_listen :: proc(socket: c.int, backlog: c.int) -> c.int --- - @(link_name="accept") _unix_accept :: proc(socket: c.int, addr: rawptr, addr_len: rawptr) -> c.int --- - @(link_name="connect") _unix_connect :: proc(socket: c.int, addr: rawptr, addr_len: socklen_t) -> c.int --- - @(link_name="bind") _unix_bind :: proc(socket: c.int, addr: rawptr, addr_len: socklen_t) -> c.int --- - @(link_name="setsockopt") _unix_setsockopt :: proc(socket: c.int, level: c.int, opt_name: c.int, opt_val: rawptr, opt_len: socklen_t) -> c.int --- - @(link_name="getsockopt") _unix_getsockopt :: proc(socket: c.int, level: c.int, opt_name: c.int, opt_val: rawptr, opt_len: ^socklen_t) -> c.int --- - @(link_name="recvfrom") _unix_recvfrom :: proc(socket: c.int, buffer: rawptr, buffer_len: c.size_t, flags: c.int, addr: rawptr, addr_len: ^socklen_t) -> c.ssize_t --- - @(link_name="recv") _unix_recv :: proc(socket: c.int, buffer: rawptr, buffer_len: c.size_t, flags: c.int) -> c.ssize_t --- - @(link_name="sendto") _unix_sendto :: proc(socket: c.int, buffer: rawptr, buffer_len: c.size_t, flags: c.int, addr: rawptr, addr_len: socklen_t) -> c.ssize_t --- - @(link_name="send") _unix_send :: proc(socket: c.int, buffer: rawptr, buffer_len: c.size_t, flags: c.int) -> c.ssize_t --- - @(link_name="shutdown") _unix_shutdown :: proc(socket: c.int, how: c.int) -> c.int --- - - @(link_name="getifaddrs") _getifaddrs :: proc(ifap: ^^ifaddrs) -> (c.int) --- - @(link_name="freeifaddrs") _freeifaddrs :: proc(ifa: ^ifaddrs) --- - - @(link_name="exit") _unix_exit :: proc(status: c.int) -> ! --- -} - -when ODIN_ARCH != .arm64 { - _unix_fdopendir :: proc {_unix_fdopendir_amd64} - _unix_readdir_r :: proc {_unix_readdir_r_amd64} -} else { - _unix_fdopendir :: proc {_unix_fdopendir_arm64} - _unix_readdir_r :: proc {_unix_readdir_r_arm64} -} - -foreign dl { - @(link_name="dlopen") _unix_dlopen :: proc(filename: cstring, flags: c.int) -> rawptr --- - @(link_name="dlsym") _unix_dlsym :: proc(handle: rawptr, symbol: cstring) -> rawptr --- - @(link_name="dlclose") _unix_dlclose :: proc(handle: rawptr) -> c.int --- - @(link_name="dlerror") _unix_dlerror :: proc() -> cstring --- -} - -@(require_results, no_instrumentation) -get_last_error :: proc "contextless" () -> Error { - return Platform_Error(__error()^) -} - -@(require_results) -get_last_error_string :: proc() -> string { - return string(_darwin_string_error(__error()^)) -} - - -@(require_results) -open :: proc(path: string, flags: int = O_RDONLY, mode: int = 0) -> (handle: Handle, err: Error) { - isDir := is_dir_path(path) - flags := flags - if isDir { - /* - @INFO(Platin): To make it impossible to use the wrong flag for dir's - as you can't write to a dir only read which makes it fail to open - */ - flags = O_RDONLY - } - - runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD() - cstr := strings.clone_to_cstring(path, context.temp_allocator) - handle = _unix_open(cstr, i32(flags), u16(mode)) - if handle == INVALID_HANDLE { - err = get_last_error() - return - } - - return -} - -fchmod :: proc(fd: Handle, mode: u16) -> Error { - return cast(Platform_Error)_unix_fchmod(fd, mode) -} - -close :: proc(fd: Handle) -> Error { - return cast(Platform_Error)_unix_close(fd) -} - -// If you read or write more than `SSIZE_MAX` bytes, most darwin implementations will return `EINVAL` -// but it is really implementation defined. `SSIZE_MAX` is also implementation defined but usually -// the max of an i32 on Darwin. -// In practice a read/write call would probably never read/write these big buffers all at once, -// which is why the number of bytes is returned and why there are procs that will call this in a -// loop for you. -// We set a max of 1GB to keep alignment and to be safe. -@(private) -MAX_RW :: 1 << 30 - -write :: proc(fd: Handle, data: []byte) -> (int, Error) { - if len(data) == 0 { - return 0, nil - } - - to_write := min(c.size_t(len(data)), MAX_RW) - - bytes_written := _unix_write(fd, raw_data(data), to_write) - if bytes_written < 0 { - return -1, get_last_error() - } - return bytes_written, nil -} - -read :: proc(fd: Handle, data: []u8) -> (int, Error) { - if len(data) == 0 { - return 0, nil - } - - to_read := min(c.size_t(len(data)), MAX_RW) - - bytes_read := _unix_read(fd, raw_data(data), to_read) - if bytes_read < 0 { - return -1, get_last_error() - } - return bytes_read, nil -} - -read_at :: proc(fd: Handle, data: []byte, offset: i64) -> (int, Error) { - if len(data) == 0 { - return 0, nil - } - - to_read := min(c.size_t(len(data)), MAX_RW) - - bytes_read := _unix_pread(fd, raw_data(data), to_read, offset) - if bytes_read < 0 { - return -1, get_last_error() - } - return bytes_read, nil -} - -write_at :: proc(fd: Handle, data: []byte, offset: i64) -> (int, Error) { - if len(data) == 0 { - return 0, nil - } - - to_write := min(c.size_t(len(data)), MAX_RW) - - bytes_written := _unix_pwrite(fd, raw_data(data), to_write, offset) - if bytes_written < 0 { - return -1, get_last_error() - } - return bytes_written, nil -} - -seek :: proc(fd: Handle, offset: i64, whence: int) -> (i64, Error) { - assert(fd != -1) - switch whence { - case SEEK_SET, SEEK_CUR, SEEK_END: - break - case: - return 0, .Invalid_Whence - } - - final_offset := i64(_unix_lseek(fd, int(offset), c.int(whence))) - if final_offset == -1 { - errno := get_last_error() - switch errno { - case .EINVAL: - return 0, .Invalid_Offset - } - return 0, errno - } - return final_offset, nil -} - -@(require_results) -file_size :: proc(fd: Handle) -> (i64, Error) { - prev, _ := seek(fd, 0, SEEK_CUR) - size, err := seek(fd, 0, SEEK_END) - seek(fd, prev, SEEK_SET) - return i64(size), err -} - - - -// NOTE(bill): Uses startup to initialize it -stdin: Handle = 0 // get_std_handle(win32.STD_INPUT_HANDLE); -stdout: Handle = 1 // get_std_handle(win32.STD_OUTPUT_HANDLE); -stderr: Handle = 2 // get_std_handle(win32.STD_ERROR_HANDLE); - -@(require_results) -last_write_time :: proc(fd: Handle) -> (time: File_Time, err: Error) { - s := _fstat(fd) or_return - modified := s.modified.seconds * 1_000_000_000 + s.modified.nanoseconds - return File_Time(modified), nil -} - -@(require_results) -last_write_time_by_name :: proc(name: string) -> (time: File_Time, err: Error) { - s := _stat(name) or_return - modified := s.modified.seconds * 1_000_000_000 + s.modified.nanoseconds - return File_Time(modified), nil -} - - -@(require_results) -is_path_separator :: proc(r: rune) -> bool { - return r == '/' -} - -@(require_results) -is_file_handle :: proc(fd: Handle) -> bool { - s, err := _fstat(fd) - if err != nil { - return false - } - return S_ISREG(s.mode) -} - -@(require_results) -is_file_path :: proc(path: string, follow_links: bool = true) -> bool { - s: OS_Stat - err: Error - if follow_links { - s, err = _stat(path) - } else { - s, err = _lstat(path) - } - if err != nil { - return false - } - return S_ISREG(s.mode) -} - - -@(require_results) -is_dir_handle :: proc(fd: Handle) -> bool { - s, err := _fstat(fd) - if err != nil { - return false - } - return S_ISDIR(s.mode) -} - -@(require_results) -is_dir_path :: proc(path: string, follow_links: bool = true) -> bool { - s: OS_Stat - err: Error - if follow_links { - s, err = _stat(path) - } else { - s, err = _lstat(path) - } - if err != nil { - return false - } - return S_ISDIR(s.mode) -} - -is_file :: proc {is_file_path, is_file_handle} -is_dir :: proc {is_dir_path, is_dir_handle} - -@(require_results) -exists :: proc(path: string) -> bool { - runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD() - cpath := strings.clone_to_cstring(path, context.temp_allocator) - res := _unix_access(cpath, O_RDONLY) - return res == 0 -} - -rename :: proc(old: string, new: string) -> bool { - runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD() - old_cstr := strings.clone_to_cstring(old, context.temp_allocator) - new_cstr := strings.clone_to_cstring(new, context.temp_allocator) - return _unix_rename(old_cstr, new_cstr) != -1 -} - -remove :: proc(path: string) -> Error { - runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD() - path_cstr := strings.clone_to_cstring(path, context.temp_allocator) - res := _unix_remove(path_cstr) - if res == -1 { - return get_last_error() - } - return nil -} - -@(private, require_results) -_stat :: proc(path: string) -> (OS_Stat, Error) { - runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD() - cstr := strings.clone_to_cstring(path, context.temp_allocator) - - s: OS_Stat - result := _unix_stat(cstr, &s) - if result == -1 { - return s, get_last_error() - } - return s, nil -} - -@(private, require_results) -_lstat :: proc(path: string) -> (OS_Stat, Error) { - runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD() - cstr := strings.clone_to_cstring(path, context.temp_allocator) - - s: OS_Stat - result := _unix_lstat(cstr, &s) - if result == -1 { - return s, get_last_error() - } - return s, nil -} - -@(private, require_results) -_fstat :: proc(fd: Handle) -> (OS_Stat, Error) { - s: OS_Stat - result := _unix_fstat(fd, &s) - if result == -1 { - return s, get_last_error() - } - return s, nil -} - -@(private, require_results) -_fdopendir :: proc(fd: Handle) -> (Dir, Error) { - dirp := _unix_fdopendir(fd) - if dirp == cast(Dir)nil { - return nil, get_last_error() - } - return dirp, nil -} - -@(private) -_closedir :: proc(dirp: Dir) -> Error { - rc := _unix_closedir(dirp) - if rc != 0 { - return get_last_error() - } - return nil -} - -@(private) -_rewinddir :: proc(dirp: Dir) { - _unix_rewinddir(dirp) -} - -@(private, require_results) -_readdir :: proc(dirp: Dir) -> (entry: Dirent, err: Error, end_of_stream: bool) { - result: ^Dirent - rc := _unix_readdir_r(dirp, &entry, &result) - - if rc != 0 { - err = get_last_error() - return - } - - if result == nil { - end_of_stream = true - return - } - end_of_stream = false - - return -} - -@(private, require_results) -_readlink :: proc(path: string) -> (string, Error) { - runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD(ignore = context.temp_allocator == context.allocator) - path_cstr := strings.clone_to_cstring(path, context.temp_allocator) - - bufsz : uint = 256 - buf := make([]byte, bufsz) - for { - rc := _unix_readlink(path_cstr, &(buf[0]), bufsz) - if rc == -1 { - delete(buf) - return "", get_last_error() - } else if rc == int(bufsz) { - // NOTE(laleksic, 2021-01-21): Any cleaner way to resize the slice? - bufsz *= 2 - delete(buf) - buf = make([]byte, bufsz) - } else { - return strings.string_from_ptr(&buf[0], rc), nil - } - } -} - -@(private, require_results) -_dup :: proc(fd: Handle) -> (Handle, Error) { - dup := _unix_dup(fd) - if dup == -1 { - return INVALID_HANDLE, get_last_error() - } - return dup, nil -} - -@(require_results) -absolute_path_from_handle :: proc(fd: Handle) -> (path: string, err: Error) { - buf: [DARWIN_MAXPATHLEN]byte - _ = fcntl(int(fd), F_GETPATH, int(uintptr(&buf[0]))) or_return - return strings.clone_from_cstring(cstring(&buf[0])) -} - -@(require_results) -absolute_path_from_relative :: proc(rel: string, allocator := context.allocator) -> (path: string, err: Error) { - rel := rel - if rel == "" { - rel = "." - } - - runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD(ignore = context.temp_allocator == context.allocator) - rel_cstr := strings.clone_to_cstring(rel, context.temp_allocator) - - path_ptr := _unix_realpath(rel_cstr, nil) - if path_ptr == nil { - return "", get_last_error() - } - defer _unix_free(rawptr(path_ptr)) - - return strings.clone(string(path_ptr), allocator) -} - -access :: proc(path: string, mask: int) -> bool { - runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD() - cstr := strings.clone_to_cstring(path, context.temp_allocator) - return _unix_access(cstr, c.int(mask)) == 0 -} - -flush :: proc(fd: Handle) -> Error { - return cast(Platform_Error)_unix_fsync(fd) -} - -@(require_results) -lookup_env_alloc :: proc(key: string, allocator := context.allocator) -> (value: string, found: bool) { - runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD(ignore = context.temp_allocator == allocator) - path_str := strings.clone_to_cstring(key, context.temp_allocator) - // NOTE(tetra): Lifetime of 'cstr' is unclear, but _unix_free(cstr) segfaults. - cstr := _unix_getenv(path_str) - if cstr == nil { - return "", false - } - return strings.clone(string(cstr), allocator), true -} - -@(require_results) -lookup_env_buffer :: proc(buf: []u8, key: string) -> (value: string, err: Error) { - if len(key) + 1 > len(buf) { - return "", .Buffer_Full - } else { - copy(buf, key) - buf[len(key)] = 0 - } - - if value = string(_unix_getenv(cstring(raw_data(buf)))); value == "" { - return "", .Env_Var_Not_Found - } else { - if len(value) > len(buf) { - return "", .Buffer_Full - } else { - copy(buf, value) - return string(buf[:len(value)]), nil - } - } -} -lookup_env :: proc{lookup_env_alloc, lookup_env_buffer} - -@(require_results) -get_env_alloc :: proc(key: string, allocator := context.allocator) -> (value: string) { - value, _ = lookup_env(key, allocator) - return -} - -@(require_results) -get_env_buf :: proc(buf: []u8, key: string) -> (value: string) { - value, _ = lookup_env(buf, key) - return -} -get_env :: proc{get_env_alloc, get_env_buf} - -set_env :: proc(key, value: string) -> Error { - runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD() - key_cstring := strings.clone_to_cstring(key, context.temp_allocator) - value_cstring := strings.clone_to_cstring(value, context.temp_allocator) - res := _unix_setenv(key_cstring, value_cstring, 1) - if res < 0 { - return get_last_error() - } - return nil -} - -unset_env :: proc(key: string) -> Error { - runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD() - s := strings.clone_to_cstring(key, context.temp_allocator) - res := _unix_unsetenv(s) - if res < 0 { - return get_last_error() - } - return nil -} - -@(require_results) -get_current_directory :: proc(allocator := context.allocator) -> string { - context.allocator = allocator - page_size := get_page_size() // NOTE(tetra): See note in os_linux.odin/get_current_directory. - buf := make([dynamic]u8, page_size) - for { - cwd := _unix_getcwd(cstring(raw_data(buf)), c.size_t(len(buf))) - if cwd != nil { - return string(cwd) - } - if get_last_error() != ERANGE { - delete(buf) - return "" - } - resize(&buf, len(buf)+page_size) - } - unreachable() -} - -set_current_directory :: proc(path: string) -> (err: Error) { - runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD() - cstr := strings.clone_to_cstring(path, context.temp_allocator) - res := _unix_chdir(cstr) - if res == -1 { - return get_last_error() - } - return nil -} - -make_directory :: proc(path: string, mode: u16 = 0o775) -> Error { - runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD() - path_cstr := strings.clone_to_cstring(path, context.temp_allocator) - res := _unix_mkdir(path_cstr, mode) - if res == -1 { - return get_last_error() - } - return nil -} - -exit :: proc "contextless" (code: int) -> ! { - runtime._cleanup_runtime_contextless() - _unix_exit(i32(code)) -} - -@(require_results) -current_thread_id :: proc "contextless" () -> int { - tid: u64 - // NOTE(Oskar): available from OSX 10.6 and iOS 3.2. - // For older versions there is `syscall(SYS_thread_selfid)`, but not really - // the same thing apparently. - foreign pthread { pthread_threadid_np :: proc "c" (rawptr, ^u64) -> c.int --- } - pthread_threadid_np(nil, &tid) - return int(tid) -} - -@(require_results) -dlopen :: proc(filename: string, flags: int) -> rawptr { - runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD() - cstr := strings.clone_to_cstring(filename, context.temp_allocator) - handle := _unix_dlopen(cstr, c.int(flags)) - return handle -} -@(require_results) -dlsym :: proc(handle: rawptr, symbol: string) -> rawptr { - assert(handle != nil) - runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD() - cstr := strings.clone_to_cstring(symbol, context.temp_allocator) - proc_handle := _unix_dlsym(handle, cstr) - return proc_handle -} -dlclose :: proc(handle: rawptr) -> bool { - assert(handle != nil) - return _unix_dlclose(handle) == 0 -} -dlerror :: proc() -> string { - return string(_unix_dlerror()) -} - -@(require_results) -get_page_size :: proc() -> int { - // NOTE(tetra): The page size never changes, so why do anything complicated - // if we don't have to. - @static page_size := -1 - if page_size != -1 { - return page_size - } - - page_size = int(_unix_getpagesize()) - return page_size -} - -@(private, require_results) -_processor_core_count :: proc() -> int { - count : int = 0 - count_size := size_of(count) - if _sysctlbyname("hw.logicalcpu", &count, &count_size, nil, 0) == 0 { - if count > 0 { - return count - } - } - - return 1 -} - -@(private, require_results) -_alloc_command_line_arguments :: proc "contextless" () -> []string { - context = runtime.default_context() - res := make([]string, len(runtime.args__)) - for _, i in res { - res[i] = string(runtime.args__[i]) - } - return res -} - -@(private, fini) -_delete_command_line_arguments :: proc "contextless" () { - context = runtime.default_context() - delete(args) -} - -socket :: proc(domain: int, type: int, protocol: int) -> (Socket, Error) { - result := _unix_socket(c.int(domain), c.int(type), c.int(protocol)) - if result < 0 { - return 0, get_last_error() - } - return Socket(result), nil -} - -connect :: proc(sd: Socket, addr: ^SOCKADDR, len: socklen_t) -> Error { - result := _unix_connect(c.int(sd), addr, len) - if result < 0 { - return get_last_error() - } - return nil -} - -bind :: proc(sd: Socket, addr: ^SOCKADDR, len: socklen_t) -> (Error) { - result := _unix_bind(c.int(sd), addr, len) - if result < 0 { - return get_last_error() - } - return nil -} - -accept :: proc(sd: Socket, addr: ^SOCKADDR, len: rawptr) -> (Socket, Error) { - result := _unix_accept(c.int(sd), rawptr(addr), len) - if result < 0 { - return 0, get_last_error() - } - return Socket(result), nil -} - -listen :: proc(sd: Socket, backlog: int) -> (Error) { - result := _unix_listen(c.int(sd), c.int(backlog)) - if result < 0 { - return get_last_error() - } - return nil -} - -setsockopt :: proc(sd: Socket, level: int, optname: int, optval: rawptr, optlen: socklen_t) -> Error { - result := _unix_setsockopt(c.int(sd), c.int(level), c.int(optname), optval, optlen) - if result < 0 { - return get_last_error() - } - return nil -} - -getsockopt :: proc(sd: Socket, level: int, optname: int, optval: rawptr, optlen: socklen_t) -> Error { - optlen := optlen - result := _unix_getsockopt(c.int(sd), c.int(level), c.int(optname), optval, &optlen) - if result < 0 { - return get_last_error() - } - return nil -} - -recvfrom :: proc(sd: Socket, data: []byte, flags: int, addr: ^SOCKADDR, addr_size: ^socklen_t) -> (u32, Error) { - result := _unix_recvfrom(c.int(sd), raw_data(data), len(data), c.int(flags), addr, addr_size) - if result < 0 { - return 0, get_last_error() - } - return u32(result), nil -} - -recv :: proc(sd: Socket, data: []byte, flags: int) -> (u32, Error) { - result := _unix_recv(c.int(sd), raw_data(data), len(data), c.int(flags)) - if result < 0 { - return 0, get_last_error() - } - return u32(result), nil -} - -sendto :: proc(sd: Socket, data: []u8, flags: int, addr: ^SOCKADDR, addrlen: socklen_t) -> (u32, Error) { - result := _unix_sendto(c.int(sd), raw_data(data), len(data), c.int(flags), addr, addrlen) - if result < 0 { - return 0, get_last_error() - } - return u32(result), nil -} - -send :: proc(sd: Socket, data: []byte, flags: int) -> (u32, Error) { - result := _unix_send(c.int(sd), raw_data(data), len(data), i32(flags)) - if result < 0 { - return 0, get_last_error() - } - return u32(result), nil -} - -shutdown :: proc(sd: Socket, how: int) -> (Error) { - result := _unix_shutdown(c.int(sd), c.int(how)) - if result < 0 { - return get_last_error() - } - return nil -} - -fcntl :: proc(fd: int, cmd: int, arg: int) -> (int, Error) { - result := _unix__fcntl(Handle(fd), c.int(cmd), uintptr(arg)) - if result < 0 { - return 0, get_last_error() - } - return int(result), nil -} diff --git a/core/os/old/os_freebsd.odin b/core/os/old/os_freebsd.odin deleted file mode 100644 index a1ecf2aff..000000000 --- a/core/os/old/os_freebsd.odin +++ /dev/null @@ -1,982 +0,0 @@ -package os_old - -foreign import dl "system:dl" -foreign import libc "system:c" - -import "base:runtime" -import "core:strings" -import "core:c" -import "core:sys/freebsd" - -Handle :: distinct i32 -File_Time :: distinct u64 - -INVALID_HANDLE :: ~Handle(0) - -_Platform_Error :: enum i32 { - NONE = 0, - EPERM = 1, - ENOENT = 2, - ESRCH = 3, - EINTR = 4, - EIO = 5, - ENXIO = 6, - E2BIG = 7, - ENOEXEC = 8, - EBADF = 9, - ECHILD = 10, - EBEADLK = 11, - ENOMEM = 12, - EACCESS = 13, - EFAULT = 14, - ENOTBLK = 15, - EBUSY = 16, - EEXIST = 17, - EXDEV = 18, - ENODEV = 19, - ENOTDIR = 20, - EISDIR = 21, - EINVAL = 22, - ENFILE = 23, - EMFILE = 24, - ENOTTY = 25, - ETXTBSY = 26, - EFBIG = 27, - ENOSPC = 28, - ESPIPE = 29, - EROFS = 30, - EMLINK = 31, - EPIPE = 32, - EDOM = 33, - ERANGE = 34, /* Result too large */ - EAGAIN = 35, - EINPROGRESS = 36, - EALREADY = 37, - ENOTSOCK = 38, - EDESTADDRREQ = 39, - EMSGSIZE = 40, - EPROTOTYPE = 41, - ENOPROTOOPT = 42, - EPROTONOSUPPORT = 43, - ESOCKTNOSUPPORT = 44, - EOPNOTSUPP = 45, - EPFNOSUPPORT = 46, - EAFNOSUPPORT = 47, - EADDRINUSE = 48, - EADDRNOTAVAIL = 49, - ENETDOWN = 50, - ENETUNREACH = 51, - ENETRESET = 52, - ECONNABORTED = 53, - ECONNRESET = 54, - ENOBUFS = 55, - EISCONN = 56, - ENOTCONN = 57, - ESHUTDOWN = 58, - ETIMEDOUT = 60, - ECONNREFUSED = 61, - ELOOP = 62, - ENAMETOOLING = 63, - EHOSTDOWN = 64, - EHOSTUNREACH = 65, - ENOTEMPTY = 66, - EPROCLIM = 67, - EUSERS = 68, - EDQUOT = 69, - ESTALE = 70, - EBADRPC = 72, - ERPCMISMATCH = 73, - EPROGUNAVAIL = 74, - EPROGMISMATCH = 75, - EPROCUNAVAIL = 76, - ENOLCK = 77, - ENOSYS = 78, - EFTYPE = 79, - EAUTH = 80, - ENEEDAUTH = 81, - EIDRM = 82, - ENOMSG = 83, - EOVERFLOW = 84, - ECANCELED = 85, - EILSEQ = 86, - ENOATTR = 87, - EDOOFUS = 88, - EBADMSG = 89, - EMULTIHOP = 90, - ENOLINK = 91, - EPROTO = 92, - ENOTCAPABLE = 93, - ECAPMODE = 94, - ENOTRECOVERABLE = 95, - EOWNERDEAD = 96, -} -EPERM :: Platform_Error.EPERM -ENOENT :: Platform_Error.ENOENT -ESRCH :: Platform_Error.ESRCH -EINTR :: Platform_Error.EINTR -EIO :: Platform_Error.EIO -ENXIO :: Platform_Error.ENXIO -E2BIG :: Platform_Error.E2BIG -ENOEXEC :: Platform_Error.ENOEXEC -EBADF :: Platform_Error.EBADF -ECHILD :: Platform_Error.ECHILD -EBEADLK :: Platform_Error.EBEADLK -ENOMEM :: Platform_Error.ENOMEM -EACCESS :: Platform_Error.EACCESS -EFAULT :: Platform_Error.EFAULT -ENOTBLK :: Platform_Error.ENOTBLK -EBUSY :: Platform_Error.EBUSY -EEXIST :: Platform_Error.EEXIST -EXDEV :: Platform_Error.EXDEV -ENODEV :: Platform_Error.ENODEV -ENOTDIR :: Platform_Error.ENOTDIR -EISDIR :: Platform_Error.EISDIR -EINVAL :: Platform_Error.EINVAL -ENFILE :: Platform_Error.ENFILE -EMFILE :: Platform_Error.EMFILE -ENOTTY :: Platform_Error.ENOTTY -ETXTBSY :: Platform_Error.ETXTBSY -EFBIG :: Platform_Error.EFBIG -ENOSPC :: Platform_Error.ENOSPC -ESPIPE :: Platform_Error.ESPIPE -EROFS :: Platform_Error.EROFS -EMLINK :: Platform_Error.EMLINK -EPIPE :: Platform_Error.EPIPE -EDOM :: Platform_Error.EDOM -ERANGE :: Platform_Error.ERANGE -EAGAIN :: Platform_Error.EAGAIN -EINPROGRESS :: Platform_Error.EINPROGRESS -EALREADY :: Platform_Error.EALREADY -ENOTSOCK :: Platform_Error.ENOTSOCK -EDESTADDRREQ :: Platform_Error.EDESTADDRREQ -EMSGSIZE :: Platform_Error.EMSGSIZE -EPROTOTYPE :: Platform_Error.EPROTOTYPE -ENOPROTOOPT :: Platform_Error.ENOPROTOOPT -EPROTONOSUPPORT :: Platform_Error.EPROTONOSUPPORT -ESOCKTNOSUPPORT :: Platform_Error.ESOCKTNOSUPPORT -EOPNOTSUPP :: Platform_Error.EOPNOTSUPP -EPFNOSUPPORT :: Platform_Error.EPFNOSUPPORT -EAFNOSUPPORT :: Platform_Error.EAFNOSUPPORT -EADDRINUSE :: Platform_Error.EADDRINUSE -EADDRNOTAVAIL :: Platform_Error.EADDRNOTAVAIL -ENETDOWN :: Platform_Error.ENETDOWN -ENETUNREACH :: Platform_Error.ENETUNREACH -ENETRESET :: Platform_Error.ENETRESET -ECONNABORTED :: Platform_Error.ECONNABORTED -ECONNRESET :: Platform_Error.ECONNRESET -ENOBUFS :: Platform_Error.ENOBUFS -EISCONN :: Platform_Error.EISCONN -ENOTCONN :: Platform_Error.ENOTCONN -ESHUTDOWN :: Platform_Error.ESHUTDOWN -ETIMEDOUT :: Platform_Error.ETIMEDOUT -ECONNREFUSED :: Platform_Error.ECONNREFUSED -ELOOP :: Platform_Error.ELOOP -ENAMETOOLING :: Platform_Error.ENAMETOOLING -EHOSTDOWN :: Platform_Error.EHOSTDOWN -EHOSTUNREACH :: Platform_Error.EHOSTUNREACH -ENOTEMPTY :: Platform_Error.ENOTEMPTY -EPROCLIM :: Platform_Error.EPROCLIM -EUSERS :: Platform_Error.EUSERS -EDQUOT :: Platform_Error.EDQUOT -ESTALE :: Platform_Error.ESTALE -EBADRPC :: Platform_Error.EBADRPC -ERPCMISMATCH :: Platform_Error.ERPCMISMATCH -EPROGUNAVAIL :: Platform_Error.EPROGUNAVAIL -EPROGMISMATCH :: Platform_Error.EPROGMISMATCH -EPROCUNAVAIL :: Platform_Error.EPROCUNAVAIL -ENOLCK :: Platform_Error.ENOLCK -ENOSYS :: Platform_Error.ENOSYS -EFTYPE :: Platform_Error.EFTYPE -EAUTH :: Platform_Error.EAUTH -ENEEDAUTH :: Platform_Error.ENEEDAUTH -EIDRM :: Platform_Error.EIDRM -ENOMSG :: Platform_Error.ENOMSG -EOVERFLOW :: Platform_Error.EOVERFLOW -ECANCELED :: Platform_Error.ECANCELED -EILSEQ :: Platform_Error.EILSEQ -ENOATTR :: Platform_Error.ENOATTR -EDOOFUS :: Platform_Error.EDOOFUS -EBADMSG :: Platform_Error.EBADMSG -EMULTIHOP :: Platform_Error.EMULTIHOP -ENOLINK :: Platform_Error.ENOLINK -EPROTO :: Platform_Error.EPROTO -ENOTCAPABLE :: Platform_Error.ENOTCAPABLE -ECAPMODE :: Platform_Error.ECAPMODE -ENOTRECOVERABLE :: Platform_Error.ENOTRECOVERABLE -EOWNERDEAD :: Platform_Error.EOWNERDEAD - -O_RDONLY :: 0x00000 -O_WRONLY :: 0x00001 -O_RDWR :: 0x00002 -O_NONBLOCK :: 0x00004 -O_APPEND :: 0x00008 -O_ASYNC :: 0x00040 -O_SYNC :: 0x00080 -O_CREATE :: 0x00200 -O_TRUNC :: 0x00400 -O_EXCL :: 0x00800 -O_NOCTTY :: 0x08000 -O_CLOEXEC :: 0100000 - - -SEEK_DATA :: 3 -SEEK_HOLE :: 4 -SEEK_MAX :: SEEK_HOLE - -// NOTE: These are OS specific! -// Do not mix these up! -RTLD_LAZY :: 0x001 -RTLD_NOW :: 0x002 -//RTLD_BINDING_MASK :: 0x3 // Called MODEMASK in dlfcn.h -RTLD_GLOBAL :: 0x100 -RTLD_LOCAL :: 0x000 -RTLD_TRACE :: 0x200 -RTLD_NODELETE :: 0x01000 -RTLD_NOLOAD :: 0x02000 - -MAX_PATH :: 1024 - -KINFO_FILE_SIZE :: 1392 - -args := _alloc_command_line_arguments() - -Unix_File_Time :: struct { - seconds: time_t, - nanoseconds: c.long, -} - -dev_t :: u64 -ino_t :: u64 -nlink_t :: u64 -off_t :: i64 -mode_t :: u16 -pid_t :: u32 -uid_t :: u32 -gid_t :: u32 -blkcnt_t :: i64 -blksize_t :: i32 -fflags_t :: u32 - -when ODIN_ARCH == .amd64 || ODIN_ARCH == .arm64 /* LP64 */ { - time_t :: i64 -} else { - time_t :: i32 -} - - -OS_Stat :: struct { - device_id: dev_t, - serial: ino_t, - nlink: nlink_t, - mode: mode_t, - _padding0: i16, - uid: uid_t, - gid: gid_t, - _padding1: i32, - rdev: dev_t, - - last_access: Unix_File_Time, - modified: Unix_File_Time, - status_change: Unix_File_Time, - birthtime: Unix_File_Time, - - size: off_t, - blocks: blkcnt_t, - block_size: blksize_t, - - flags: fflags_t, - gen: u64, - lspare: [10]u64, -} - -KInfo_File :: struct { - structsize: c.int, - type: c.int, - fd: c.int, - ref_count: c.int, - flags: c.int, - pad0: c.int, - offset: i64, - - // NOTE(Feoramund): This field represents a complicated union that I am - // avoiding implementing for now. I only need the path data below. - _union: [336]byte, - - path: [MAX_PATH]c.char, -} - -// since FreeBSD v12 -Dirent :: struct { - ino: ino_t, - off: off_t, - reclen: u16, - type: u8, - _pad0: u8, - namlen: u16, - _pad1: u16, - name: [256]byte, -} - -Dir :: distinct rawptr // DIR* - -// File type -S_IFMT :: 0o170000 // Type of file mask -S_IFIFO :: 0o010000 // Named pipe (fifo) -S_IFCHR :: 0o020000 // Character special -S_IFDIR :: 0o040000 // Directory -S_IFBLK :: 0o060000 // Block special -S_IFREG :: 0o100000 // Regular -S_IFLNK :: 0o120000 // Symbolic link -S_IFSOCK :: 0o140000 // Socket -//S_ISVTX :: 0o001000 // Save swapped text even after use - -// File mode -// Read, write, execute/search by owner -S_IRWXU :: 0o0700 // RWX mask for owner -S_IRUSR :: 0o0400 // R for owner -S_IWUSR :: 0o0200 // W for owner -S_IXUSR :: 0o0100 // X for owner - - // Read, write, execute/search by group -S_IRWXG :: 0o0070 // RWX mask for group -S_IRGRP :: 0o0040 // R for group -S_IWGRP :: 0o0020 // W for group -S_IXGRP :: 0o0010 // X for group - - // Read, write, execute/search by others -S_IRWXO :: 0o0007 // RWX mask for other -S_IROTH :: 0o0004 // R for other -S_IWOTH :: 0o0002 // W for other -S_IXOTH :: 0o0001 // X for other - -S_ISUID :: 0o4000 // Set user id on execution -S_ISGID :: 0o2000 // Set group id on execution -S_ISVTX :: 0o1000 // Directory restrcted delete - - -@(require_results) S_ISLNK :: #force_inline proc(m: mode_t) -> bool { return (m & S_IFMT) == S_IFLNK } -@(require_results) S_ISREG :: #force_inline proc(m: mode_t) -> bool { return (m & S_IFMT) == S_IFREG } -@(require_results) S_ISDIR :: #force_inline proc(m: mode_t) -> bool { return (m & S_IFMT) == S_IFDIR } -@(require_results) S_ISCHR :: #force_inline proc(m: mode_t) -> bool { return (m & S_IFMT) == S_IFCHR } -@(require_results) S_ISBLK :: #force_inline proc(m: mode_t) -> bool { return (m & S_IFMT) == S_IFBLK } -@(require_results) S_ISFIFO :: #force_inline proc(m: mode_t) -> bool { return (m & S_IFMT) == S_IFIFO } -@(require_results) S_ISSOCK :: #force_inline proc(m: mode_t) -> bool { return (m & S_IFMT) == S_IFSOCK } - -F_OK :: 0 // Test for file existance -X_OK :: 1 // Test for execute permission -W_OK :: 2 // Test for write permission -R_OK :: 4 // Test for read permission - -F_KINFO :: 22 - -foreign libc { - @(link_name="__error") __Error_location :: proc() -> ^c.int --- - - @(link_name="open") _unix_open :: proc(path: cstring, flags: c.int, #c_vararg mode: ..u16) -> Handle --- - @(link_name="close") _unix_close :: proc(fd: Handle) -> c.int --- - @(link_name="read") _unix_read :: proc(fd: Handle, buf: rawptr, size: c.size_t) -> c.ssize_t --- - @(link_name="write") _unix_write :: proc(fd: Handle, buf: rawptr, size: c.size_t) -> c.ssize_t --- - @(link_name="lseek") _unix_seek :: proc(fd: Handle, offset: i64, whence: c.int) -> i64 --- - @(link_name="getpagesize") _unix_getpagesize :: proc() -> c.int --- - @(link_name="stat") _unix_stat :: proc(path: cstring, stat: ^OS_Stat) -> c.int --- - @(link_name="lstat") _unix_lstat :: proc(path: cstring, sb: ^OS_Stat) -> c.int --- - @(link_name="fstat") _unix_fstat :: proc(fd: Handle, stat: ^OS_Stat) -> c.int --- - @(link_name="readlink") _unix_readlink :: proc(path: cstring, buf: ^byte, bufsiz: c.size_t) -> c.ssize_t --- - @(link_name="access") _unix_access :: proc(path: cstring, mask: c.int) -> c.int --- - @(link_name="getcwd") _unix_getcwd :: proc(buf: cstring, len: c.size_t) -> cstring --- - @(link_name="chdir") _unix_chdir :: proc(buf: cstring) -> c.int --- - @(link_name="rename") _unix_rename :: proc(old, new: cstring) -> c.int --- - @(link_name="unlink") _unix_unlink :: proc(path: cstring) -> c.int --- - @(link_name="rmdir") _unix_rmdir :: proc(path: cstring) -> c.int --- - @(link_name="mkdir") _unix_mkdir :: proc(path: cstring, mode: mode_t) -> c.int --- - @(link_name="fcntl") _unix_fcntl :: proc(fd: Handle, cmd: c.int, #c_vararg args: ..any) -> c.int --- - @(link_name="dup") _unix_dup :: proc(fd: Handle) -> Handle --- - - @(link_name="fdopendir") _unix_fdopendir :: proc(fd: Handle) -> Dir --- - @(link_name="closedir") _unix_closedir :: proc(dirp: Dir) -> c.int --- - @(link_name="rewinddir") _unix_rewinddir :: proc(dirp: Dir) --- - @(link_name="readdir_r") _unix_readdir_r :: proc(dirp: Dir, entry: ^Dirent, result: ^^Dirent) -> c.int --- - - @(link_name="malloc") _unix_malloc :: proc(size: c.size_t) -> rawptr --- - @(link_name="calloc") _unix_calloc :: proc(num, size: c.size_t) -> rawptr --- - @(link_name="free") _unix_free :: proc(ptr: rawptr) --- - @(link_name="realloc") _unix_realloc :: proc(ptr: rawptr, size: c.size_t) -> rawptr --- - - @(link_name="getenv") _unix_getenv :: proc(cstring) -> cstring --- - @(link_name="realpath") _unix_realpath :: proc(path: cstring, resolved_path: [^]byte = nil) -> cstring --- - @(link_name="sysctlbyname") _sysctlbyname :: proc(path: cstring, oldp: rawptr, oldlenp: rawptr, newp: rawptr, newlen: int) -> c.int --- - - @(link_name="exit") _unix_exit :: proc(status: c.int) -> ! --- -} -foreign dl { - @(link_name="dlopen") _unix_dlopen :: proc(filename: cstring, flags: c.int) -> rawptr --- - @(link_name="dlsym") _unix_dlsym :: proc(handle: rawptr, symbol: cstring) -> rawptr --- - @(link_name="dlclose") _unix_dlclose :: proc(handle: rawptr) -> c.int --- - @(link_name="dlerror") _unix_dlerror :: proc() -> cstring --- - - @(link_name="pthread_getthreadid_np") pthread_getthreadid_np :: proc() -> c.int --- -} - -@(require_results) -is_path_separator :: proc(r: rune) -> bool { - return r == '/' -} - -@(require_results, no_instrumentation) -get_last_error :: proc "contextless" () -> Error { - return Platform_Error(__Error_location()^) -} - -@(require_results) -open :: proc(path: string, flags: int = O_RDONLY, mode: int = 0) -> (Handle, Error) { - runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD() - cstr := strings.clone_to_cstring(path, context.temp_allocator) - handle := _unix_open(cstr, c.int(flags), u16(mode)) - if handle == -1 { - return INVALID_HANDLE, get_last_error() - } - return handle, nil -} - -close :: proc(fd: Handle) -> Error { - result := _unix_close(fd) - if result == -1 { - return get_last_error() - } - return nil -} - -flush :: proc(fd: Handle) -> Error { - return cast(_Platform_Error)freebsd.fsync(cast(freebsd.Fd)fd) -} - -// If you read or write more than `INT_MAX` bytes, FreeBSD returns `EINVAL`. -// In practice a read/write call would probably never read/write these big buffers all at once, -// which is why the number of bytes is returned and why there are procs that will call this in a -// loop for you. -// We set a max of 1GB to keep alignment and to be safe. -@(private) -MAX_RW :: 1 << 30 - -read :: proc(fd: Handle, data: []byte) -> (int, Error) { - to_read := min(c.size_t(len(data)), MAX_RW) - bytes_read := _unix_read(fd, &data[0], to_read) - if bytes_read == -1 { - return -1, get_last_error() - } - return int(bytes_read), nil -} - -write :: proc(fd: Handle, data: []byte) -> (int, Error) { - if len(data) == 0 { - return 0, nil - } - - to_write := min(c.size_t(len(data)), MAX_RW) - bytes_written := _unix_write(fd, &data[0], to_write) - if bytes_written == -1 { - return -1, get_last_error() - } - return int(bytes_written), nil -} - -read_at :: proc(fd: Handle, data: []byte, offset: i64) -> (n: int, err: Error) { - if len(data) == 0 { - return 0, nil - } - - to_read := min(uint(len(data)), MAX_RW) - - bytes_read, errno := freebsd.pread(cast(freebsd.Fd)fd, data[:to_read], cast(freebsd.off_t)offset) - - return bytes_read, cast(_Platform_Error)errno -} - -write_at :: proc(fd: Handle, data: []byte, offset: i64) -> (n: int, err: Error) { - if len(data) == 0 { - return 0, nil - } - - to_write := min(uint(len(data)), MAX_RW) - - bytes_written, errno := freebsd.pwrite(cast(freebsd.Fd)fd, data[:to_write], cast(freebsd.off_t)offset) - - return bytes_written, cast(_Platform_Error)errno -} - -seek :: proc(fd: Handle, offset: i64, whence: int) -> (i64, Error) { - switch whence { - case SEEK_SET, SEEK_CUR, SEEK_END: - break - case: - return 0, .Invalid_Whence - } - res := _unix_seek(fd, offset, c.int(whence)) - if res == -1 { - errno := get_last_error() - switch errno { - case .EINVAL: - return 0, .Invalid_Offset - case: - return 0, errno - } - } - return res, nil -} - -@(require_results) -file_size :: proc(fd: Handle) -> (size: i64, err: Error) { - size = -1 - s := _fstat(fd) or_return - size = s.size - return -} - -rename :: proc(old_path, new_path: string) -> Error { - runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD() - old_path_cstr := strings.clone_to_cstring(old_path, context.temp_allocator) - new_path_cstr := strings.clone_to_cstring(new_path, context.temp_allocator) - res := _unix_rename(old_path_cstr, new_path_cstr) - if res == -1 { - return get_last_error() - } - return nil -} - -remove :: proc(path: string) -> Error { - runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD() - path_cstr := strings.clone_to_cstring(path, context.temp_allocator) - res := _unix_unlink(path_cstr) - if res == -1 { - return get_last_error() - } - return nil -} - -make_directory :: proc(path: string, mode: mode_t = 0o775) -> Error { - runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD() - path_cstr := strings.clone_to_cstring(path, context.temp_allocator) - res := _unix_mkdir(path_cstr, mode) - if res == -1 { - return get_last_error() - } - return nil -} - -remove_directory :: proc(path: string) -> Error { - runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD() - path_cstr := strings.clone_to_cstring(path, context.temp_allocator) - res := _unix_rmdir(path_cstr) - if res == -1 { - return get_last_error() - } - return nil -} - -@(require_results) -is_file_handle :: proc(fd: Handle) -> bool { - s, err := _fstat(fd) - if err != nil { - return false - } - return S_ISREG(s.mode) -} - -@(require_results) -is_file_path :: proc(path: string, follow_links: bool = true) -> bool { - s: OS_Stat - err: Error - if follow_links { - s, err = _stat(path) - } else { - s, err = _lstat(path) - } - if err != nil { - return false - } - return S_ISREG(s.mode) -} - -@(require_results) -is_dir_handle :: proc(fd: Handle) -> bool { - s, err := _fstat(fd) - if err != nil { - return false - } - return S_ISDIR(s.mode) -} - -@(require_results) -is_dir_path :: proc(path: string, follow_links: bool = true) -> bool { - s: OS_Stat - err: Error - if follow_links { - s, err = _stat(path) - } else { - s, err = _lstat(path) - } - if err != nil { - return false - } - return S_ISDIR(s.mode) -} - -is_file :: proc {is_file_path, is_file_handle} -is_dir :: proc {is_dir_path, is_dir_handle} - -@(require_results) -exists :: proc(path: string) -> bool { - runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD() - cpath := strings.clone_to_cstring(path, context.temp_allocator) - res := _unix_access(cpath, O_RDONLY) - return res == 0 -} - -// NOTE(bill): Uses startup to initialize it - -stdin: Handle = 0 -stdout: Handle = 1 -stderr: Handle = 2 - -/* TODO(zangent): Implement these! -last_write_time :: proc(fd: Handle) -> File_Time {} -last_write_time_by_name :: proc(name: string) -> File_Time {} -*/ -@(require_results) -last_write_time :: proc(fd: Handle) -> (File_Time, Error) { - s, err := _fstat(fd) - if err != nil { - return 0, err - } - modified := s.modified.seconds * 1_000_000_000 + s.modified.nanoseconds - return File_Time(modified), nil -} - -@(require_results) -last_write_time_by_name :: proc(name: string) -> (File_Time, Error) { - s, err := _stat(name) - if err != nil { - return 0, err - } - modified := s.modified.seconds * 1_000_000_000 + s.modified.nanoseconds - return File_Time(modified), nil -} - -@(private, require_results, no_sanitize_memory) -_stat :: proc(path: string) -> (OS_Stat, Error) { - runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD() - cstr := strings.clone_to_cstring(path, context.temp_allocator) - s: OS_Stat = --- - result := _unix_lstat(cstr, &s) - if result == -1 { - return s, get_last_error() - } - return s, nil -} - -@(private, require_results, no_sanitize_memory) -_lstat :: proc(path: string) -> (OS_Stat, Error) { - runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD() - cstr := strings.clone_to_cstring(path, context.temp_allocator) - - // deliberately uninitialized - s: OS_Stat = --- - res := _unix_lstat(cstr, &s) - if res == -1 { - return s, get_last_error() - } - return s, nil -} - -@(private, require_results, no_sanitize_memory) -_fstat :: proc(fd: Handle) -> (OS_Stat, Error) { - s: OS_Stat = --- - result := _unix_fstat(fd, &s) - if result == -1 { - return s, get_last_error() - } - return s, nil -} - -@(private, require_results) -_fdopendir :: proc(fd: Handle) -> (Dir, Error) { - dirp := _unix_fdopendir(fd) - if dirp == cast(Dir)nil { - return nil, get_last_error() - } - return dirp, nil -} - -@(private) -_closedir :: proc(dirp: Dir) -> Error { - rc := _unix_closedir(dirp) - if rc != 0 { - return get_last_error() - } - return nil -} - -@(private) -_rewinddir :: proc(dirp: Dir) { - _unix_rewinddir(dirp) -} - -@(private, require_results) -_readdir :: proc(dirp: Dir) -> (entry: Dirent, err: Error, end_of_stream: bool) { - result: ^Dirent - rc := _unix_readdir_r(dirp, &entry, &result) - - if rc != 0 { - err = get_last_error() - return - } - - if result == nil { - end_of_stream = true - return - } - - return -} - -@(private, require_results) -_readlink :: proc(path: string) -> (string, Error) { - runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD(ignore = context.temp_allocator == context.allocator) - - path_cstr := strings.clone_to_cstring(path, context.temp_allocator) - - bufsz : uint = MAX_PATH - buf := make([]byte, MAX_PATH) - for { - rc := _unix_readlink(path_cstr, &(buf[0]), bufsz) - if rc == -1 { - delete(buf) - return "", get_last_error() - } else if rc == int(bufsz) { - bufsz += MAX_PATH - delete(buf) - buf = make([]byte, bufsz) - } else { - return strings.string_from_ptr(&buf[0], rc), nil - } - } - - return "", Error{} -} - -@(private, require_results) -_dup :: proc(fd: Handle) -> (Handle, Error) { - dup := _unix_dup(fd) - if dup == -1 { - return INVALID_HANDLE, get_last_error() - } - return dup, nil -} - -@(require_results) -absolute_path_from_handle :: proc(fd: Handle) -> (string, Error) { - // NOTE(Feoramund): The situation isn't ideal, but this was the best way I - // could find to implement this. There are a couple outstanding bug reports - // regarding the desire to retrieve an absolute path from a handle, but to - // my knowledge, there hasn't been any work done on it. - // - // https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=198570 - // - // This may be unreliable, according to a comment from 2023. - - kinfo: KInfo_File - kinfo.structsize = KINFO_FILE_SIZE - - res := _unix_fcntl(fd, F_KINFO, cast(uintptr)&kinfo) - if res == -1 { - return "", get_last_error() - } - - path := strings.clone_from_cstring_bounded(cast(cstring)&kinfo.path[0], len(kinfo.path)) - return path, nil -} - -@(require_results) -absolute_path_from_relative :: proc(rel: string, allocator := context.allocator) -> (path: string, err: Error) { - rel := rel - if rel == "" { - rel = "." - } - runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD(ignore = context.temp_allocator == context.allocator) - - rel_cstr := strings.clone_to_cstring(rel, context.temp_allocator) - - path_ptr := _unix_realpath(rel_cstr, nil) - if path_ptr == nil { - return "", get_last_error() - } - defer _unix_free(rawptr(path_ptr)) - - return strings.clone(string(path_ptr), allocator) -} - -access :: proc(path: string, mask: int) -> (bool, Error) { - runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD() - - cstr := strings.clone_to_cstring(path, context.temp_allocator) - result := _unix_access(cstr, c.int(mask)) - if result == -1 { - return false, get_last_error() - } - return true, nil -} - -@(require_results) -lookup_env_alloc :: proc(key: string, allocator := context.allocator) -> (value: string, found: bool) { - runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD(ignore = context.temp_allocator == allocator) - path_str := strings.clone_to_cstring(key, context.temp_allocator) - // NOTE(tetra): Lifetime of 'cstr' is unclear, but _unix_free(cstr) segfaults. - cstr := _unix_getenv(path_str) - if cstr == nil { - return "", false - } - return strings.clone(string(cstr), allocator), true -} - -@(require_results) -lookup_env_buffer :: proc(buf: []u8, key: string) -> (value: string, err: Error) { - if len(key) + 1 > len(buf) { - return "", .Buffer_Full - } else { - copy(buf, key) - buf[len(key)] = 0 - } - - if value = string(_unix_getenv(cstring(raw_data(buf)))); value == "" { - return "", .Env_Var_Not_Found - } else { - if len(value) > len(buf) { - return "", .Buffer_Full - } else { - copy(buf, value) - return string(buf[:len(value)]), nil - } - } -} -lookup_env :: proc{lookup_env_alloc, lookup_env_buffer} - -@(require_results) -get_env_alloc :: proc(key: string, allocator := context.allocator) -> (value: string) { - value, _ = lookup_env(key, allocator) - return -} - -@(require_results) -get_env_buf :: proc(buf: []u8, key: string) -> (value: string) { - value, _ = lookup_env(buf, key) - return -} -get_env :: proc{get_env_alloc, get_env_buf} - -@(require_results) -get_current_directory :: proc(allocator := context.allocator) -> string { - context.allocator = allocator - // NOTE(tetra): I would use PATH_MAX here, but I was not able to find - // an authoritative value for it across all systems. - // The largest value I could find was 4096, so might as well use the page size. - page_size := get_page_size() - buf := make([dynamic]u8, page_size) - #no_bounds_check for { - cwd := _unix_getcwd(cstring(&buf[0]), c.size_t(len(buf))) - if cwd != nil { - return string(cwd) - } - if get_last_error() != ERANGE { - delete(buf) - return "" - } - resize(&buf, len(buf)+page_size) - } - unreachable() -} - -set_current_directory :: proc(path: string) -> (err: Error) { - runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD() - cstr := strings.clone_to_cstring(path, context.temp_allocator) - res := _unix_chdir(cstr) - if res == -1 { - return get_last_error() - } - return nil -} - -exit :: proc "contextless" (code: int) -> ! { - runtime._cleanup_runtime_contextless() - _unix_exit(c.int(code)) -} - -@(require_results) -current_thread_id :: proc "contextless" () -> int { - return cast(int) pthread_getthreadid_np() -} - -@(require_results) -dlopen :: proc(filename: string, flags: int) -> rawptr { - runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD() - cstr := strings.clone_to_cstring(filename, context.temp_allocator) - handle := _unix_dlopen(cstr, c.int(flags)) - return handle -} -@(require_results) -dlsym :: proc(handle: rawptr, symbol: string) -> rawptr { - assert(handle != nil) - runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD() - cstr := strings.clone_to_cstring(symbol, context.temp_allocator) - proc_handle := _unix_dlsym(handle, cstr) - return proc_handle -} -dlclose :: proc(handle: rawptr) -> bool { - assert(handle != nil) - return _unix_dlclose(handle) == 0 -} -dlerror :: proc() -> string { - return string(_unix_dlerror()) -} - -@(require_results) -get_page_size :: proc() -> int { - // NOTE(tetra): The page size never changes, so why do anything complicated - // if we don't have to. - @static page_size := -1 - if page_size != -1 { - return page_size - } - - page_size = int(_unix_getpagesize()) - return page_size -} - -@(private, require_results) -_processor_core_count :: proc() -> int { - count : int = 0 - count_size := size_of(count) - if _sysctlbyname("hw.ncpu", &count, &count_size, nil, 0) == 0 { - if count > 0 { - return count - } - } - - return 1 -} - - -@(private, require_results) -_alloc_command_line_arguments :: proc "contextless" () -> []string { - context = runtime.default_context() - res := make([]string, len(runtime.args__)) - for _, i in res { - res[i] = string(runtime.args__[i]) - } - return res -} - -@(private, fini) -_delete_command_line_arguments :: proc "contextless" () { - context = runtime.default_context() - delete(args) -} diff --git a/core/os/old/os_freestanding.odin b/core/os/old/os_freestanding.odin deleted file mode 100644 index def000aae..000000000 --- a/core/os/old/os_freestanding.odin +++ /dev/null @@ -1,4 +0,0 @@ -#+build freestanding -package os_old - -#panic("package os_old does not support a freestanding target") diff --git a/core/os/old/os_js.odin b/core/os/old/os_js.odin deleted file mode 100644 index cefabbf4d..000000000 --- a/core/os/old/os_js.odin +++ /dev/null @@ -1,275 +0,0 @@ -#+build js -package os_old - -foreign import "odin_env" - -@(require_results) -is_path_separator :: proc(c: byte) -> bool { - return c == '/' || c == '\\' -} - -Handle :: distinct u32 - -stdout: Handle = 1 -stderr: Handle = 2 - -@(require_results) -open :: proc(path: string, mode: int = O_RDONLY, perm: int = 0) -> (Handle, Error) { - unimplemented("core:os procedure not supported on JS target") -} - -close :: proc(fd: Handle) -> Error { - return nil -} - -flush :: proc(fd: Handle) -> (err: Error) { - return nil -} - -write :: proc(fd: Handle, data: []byte) -> (int, Error) { - foreign odin_env { - @(link_name="write") - _write :: proc "contextless" (fd: Handle, p: []byte) --- - } - _write(fd, data) - return len(data), nil -} - -read :: proc(fd: Handle, data: []byte) -> (int, Error) { - unimplemented("core:os procedure not supported on JS target") -} - -seek :: proc(fd: Handle, offset: i64, whence: int) -> (i64, Error) { - unimplemented("core:os procedure not supported on JS target") -} - -@(require_results) -file_size :: proc(fd: Handle) -> (i64, Error) { - unimplemented("core:os procedure not supported on JS target") -} - -read_at :: proc(fd: Handle, data: []byte, offset: i64) -> (n: int, err: Error) { - unimplemented("core:os procedure not supported on JS target") -} -write_at :: proc(fd: Handle, data: []byte, offset: i64) -> (n: int, err: Error) { - unimplemented("core:os procedure not supported on JS target") -} - -@(require_results) -exists :: proc(path: string) -> bool { - unimplemented("core:os procedure not supported on JS target") -} - -@(require_results) -is_file :: proc(path: string) -> bool { - unimplemented("core:os procedure not supported on JS target") -} - -@(require_results) -is_dir :: proc(path: string) -> bool { - unimplemented("core:os procedure not supported on JS target") -} - -@(require_results) -get_current_directory :: proc(allocator := context.allocator) -> string { - unimplemented("core:os procedure not supported on JS target") -} - -set_current_directory :: proc(path: string) -> (err: Error) { - unimplemented("core:os procedure not supported on JS target") -} - - - -change_directory :: proc(path: string) -> (err: Error) { - unimplemented("core:os procedure not supported on JS target") -} - -make_directory :: proc(path: string, mode: u32 = 0) -> (err: Error) { - unimplemented("core:os procedure not supported on JS target") -} - - -remove_directory :: proc(path: string) -> (err: Error) { - unimplemented("core:os procedure not supported on JS target") -} - - -link :: proc(old_name, new_name: string) -> (err: Error) { - unimplemented("core:os procedure not supported on JS target") -} - -unlink :: proc(path: string) -> (err: Error) { - unimplemented("core:os procedure not supported on JS target") -} - - - -rename :: proc(old_path, new_path: string) -> (err: Error) { - unimplemented("core:os procedure not supported on JS target") -} - - -ftruncate :: proc(fd: Handle, length: i64) -> (err: Error) { - unimplemented("core:os procedure not supported on JS target") -} - -truncate :: proc(path: string, length: i64) -> (err: Error) { - unimplemented("core:os procedure not supported on JS target") -} - - -remove :: proc(name: string) -> Error { - unimplemented("core:os procedure not supported on JS target") -} - - -@(require_results) -pipe :: proc() -> (r, w: Handle, err: Error) { - unimplemented("core:os procedure not supported on JS target") -} - -@(require_results) -read_dir :: proc(fd: Handle, n: int, allocator := context.allocator) -> (fi: []File_Info, err: Error) { - unimplemented("core:os procedure not supported on JS target") -} - -File_Time :: distinct u64 - -_Platform_Error :: enum i32 { - NONE = 0, - FILE_NOT_FOUND = 2, - PATH_NOT_FOUND = 3, - ACCESS_DENIED = 5, - INVALID_HANDLE = 6, - NOT_ENOUGH_MEMORY = 8, - NO_MORE_FILES = 18, - HANDLE_EOF = 38, - NETNAME_DELETED = 64, - FILE_EXISTS = 80, - INVALID_PARAMETER = 87, - BROKEN_PIPE = 109, - BUFFER_OVERFLOW = 111, - INSUFFICIENT_BUFFER = 122, - MOD_NOT_FOUND = 126, - PROC_NOT_FOUND = 127, - DIR_NOT_EMPTY = 145, - ALREADY_EXISTS = 183, - ENVVAR_NOT_FOUND = 203, - MORE_DATA = 234, - OPERATION_ABORTED = 995, - IO_PENDING = 997, - NOT_FOUND = 1168, - PRIVILEGE_NOT_HELD = 1314, - WSAEACCES = 10013, - WSAECONNRESET = 10054, - - // Windows reserves errors >= 1<<29 for application use - FILE_IS_PIPE = 1<<29 + 0, - FILE_IS_NOT_DIR = 1<<29 + 1, - NEGATIVE_OFFSET = 1<<29 + 2, -} - - -INVALID_HANDLE :: ~Handle(0) - - - -O_RDONLY :: 0x00000 -O_WRONLY :: 0x00001 -O_RDWR :: 0x00002 -O_CREATE :: 0x00040 -O_EXCL :: 0x00080 -O_NOCTTY :: 0x00100 -O_TRUNC :: 0x00200 -O_NONBLOCK :: 0x00800 -O_APPEND :: 0x00400 -O_SYNC :: 0x01000 -O_ASYNC :: 0x02000 -O_CLOEXEC :: 0x80000 - - -ERROR_FILE_NOT_FOUND :: Platform_Error.FILE_NOT_FOUND -ERROR_PATH_NOT_FOUND :: Platform_Error.PATH_NOT_FOUND -ERROR_ACCESS_DENIED :: Platform_Error.ACCESS_DENIED -ERROR_INVALID_HANDLE :: Platform_Error.INVALID_HANDLE -ERROR_NOT_ENOUGH_MEMORY :: Platform_Error.NOT_ENOUGH_MEMORY -ERROR_NO_MORE_FILES :: Platform_Error.NO_MORE_FILES -ERROR_HANDLE_EOF :: Platform_Error.HANDLE_EOF -ERROR_NETNAME_DELETED :: Platform_Error.NETNAME_DELETED -ERROR_FILE_EXISTS :: Platform_Error.FILE_EXISTS -ERROR_INVALID_PARAMETER :: Platform_Error.INVALID_PARAMETER -ERROR_BROKEN_PIPE :: Platform_Error.BROKEN_PIPE -ERROR_BUFFER_OVERFLOW :: Platform_Error.BUFFER_OVERFLOW -ERROR_INSUFFICIENT_BUFFER :: Platform_Error.INSUFFICIENT_BUFFER -ERROR_MOD_NOT_FOUND :: Platform_Error.MOD_NOT_FOUND -ERROR_PROC_NOT_FOUND :: Platform_Error.PROC_NOT_FOUND -ERROR_DIR_NOT_EMPTY :: Platform_Error.DIR_NOT_EMPTY -ERROR_ALREADY_EXISTS :: Platform_Error.ALREADY_EXISTS -ERROR_ENVVAR_NOT_FOUND :: Platform_Error.ENVVAR_NOT_FOUND -ERROR_MORE_DATA :: Platform_Error.MORE_DATA -ERROR_OPERATION_ABORTED :: Platform_Error.OPERATION_ABORTED -ERROR_IO_PENDING :: Platform_Error.IO_PENDING -ERROR_NOT_FOUND :: Platform_Error.NOT_FOUND -ERROR_PRIVILEGE_NOT_HELD :: Platform_Error.PRIVILEGE_NOT_HELD -WSAEACCES :: Platform_Error.WSAEACCES -WSAECONNRESET :: Platform_Error.WSAECONNRESET - -ERROR_FILE_IS_PIPE :: General_Error.File_Is_Pipe -ERROR_FILE_IS_NOT_DIR :: General_Error.Not_Dir - -args: []string - -@(require_results) -last_write_time :: proc(fd: Handle) -> (File_Time, Error) { - unimplemented("core:os procedure not supported on JS target") -} - -@(require_results) -last_write_time_by_name :: proc(name: string) -> (File_Time, Error) { - unimplemented("core:os procedure not supported on JS target") -} - - -@(require_results) -get_page_size :: proc() -> int { - unimplemented("core:os procedure not supported on JS target") -} - -@(private, require_results) -_processor_core_count :: proc() -> int { - return 1 -} - -exit :: proc "contextless" (code: int) -> ! { - unimplemented_contextless("core:os procedure not supported on JS target") -} - -@(require_results) -current_thread_id :: proc "contextless" () -> int { - return 0 -} - -@(require_results) -lookup_env_alloc :: proc(key: string, allocator := context.allocator) -> (value: string, found: bool) { - return "", false -} - -@(require_results) -lookup_env_buffer :: proc(buf: []u8, key: string) -> (value: string, err: Error) { - return "", .Env_Var_Not_Found -} -lookup_env :: proc{lookup_env_alloc, lookup_env_buffer} - -@(require_results) -get_env_alloc :: proc(key: string, allocator := context.allocator) -> (value: string) { - value, _ = lookup_env(key, allocator) - return -} - -@(require_results) -get_env_buf :: proc(buf: []u8, key: string) -> (value: string) { - value, _ = lookup_env(buf, key) - return -} -get_env :: proc{get_env_alloc, get_env_buf} \ No newline at end of file diff --git a/core/os/old/os_linux.odin b/core/os/old/os_linux.odin deleted file mode 100644 index 504f6f5b3..000000000 --- a/core/os/old/os_linux.odin +++ /dev/null @@ -1,1222 +0,0 @@ -package os_old - -foreign import dl "system:dl" -foreign import libc "system:c" - -import "base:runtime" -import "core:strings" -import "core:c" -import "core:strconv" -import "core:sys/unix" -import "core:sys/linux" - -Handle :: distinct i32 -Pid :: distinct i32 -File_Time :: distinct u64 -Socket :: distinct int - -INVALID_HANDLE :: ~Handle(0) - -_Platform_Error :: linux.Errno -EPERM :: Platform_Error.EPERM -ENOENT :: Platform_Error.ENOENT -ESRCH :: Platform_Error.ESRCH -EINTR :: Platform_Error.EINTR -EIO :: Platform_Error.EIO -ENXIO :: Platform_Error.ENXIO -EBADF :: Platform_Error.EBADF -EAGAIN :: Platform_Error.EAGAIN -ENOMEM :: Platform_Error.ENOMEM -EACCES :: Platform_Error.EACCES -EFAULT :: Platform_Error.EFAULT -EEXIST :: Platform_Error.EEXIST -ENODEV :: Platform_Error.ENODEV -ENOTDIR :: Platform_Error.ENOTDIR -EISDIR :: Platform_Error.EISDIR -EINVAL :: Platform_Error.EINVAL -ENFILE :: Platform_Error.ENFILE -EMFILE :: Platform_Error.EMFILE -ETXTBSY :: Platform_Error.ETXTBSY -EFBIG :: Platform_Error.EFBIG -ENOSPC :: Platform_Error.ENOSPC -ESPIPE :: Platform_Error.ESPIPE -EROFS :: Platform_Error.EROFS -EPIPE :: Platform_Error.EPIPE - -ERANGE :: Platform_Error.ERANGE /* Result too large */ -EDEADLK :: Platform_Error.EDEADLK /* Resource deadlock would occur */ -ENAMETOOLONG :: Platform_Error.ENAMETOOLONG /* File name too long */ -ENOLCK :: Platform_Error.ENOLCK /* No record locks available */ - -ENOSYS :: Platform_Error.ENOSYS /* Invalid system call number */ - -ENOTEMPTY :: Platform_Error.ENOTEMPTY /* Directory not empty */ -ELOOP :: Platform_Error.ELOOP /* Too many symbolic links encountered */ -EWOULDBLOCK :: Platform_Error.EWOULDBLOCK /* Operation would block */ -ENOMSG :: Platform_Error.ENOMSG /* No message of desired type */ -EIDRM :: Platform_Error.EIDRM /* Identifier removed */ -ECHRNG :: Platform_Error.ECHRNG /* Channel number out of range */ -EL2NSYNC :: Platform_Error.EL2NSYNC /* Level 2 not synchronized */ -EL3HLT :: Platform_Error.EL3HLT /* Level 3 halted */ -EL3RST :: Platform_Error.EL3RST /* Level 3 reset */ -ELNRNG :: Platform_Error.ELNRNG /* Link number out of range */ -EUNATCH :: Platform_Error.EUNATCH /* Protocol driver not attached */ -ENOCSI :: Platform_Error.ENOCSI /* No CSI structure available */ -EL2HLT :: Platform_Error.EL2HLT /* Level 2 halted */ -EBADE :: Platform_Error.EBADE /* Invalid exchange */ -EBADR :: Platform_Error.EBADR /* Invalid request descriptor */ -EXFULL :: Platform_Error.EXFULL /* Exchange full */ -ENOANO :: Platform_Error.ENOANO /* No anode */ -EBADRQC :: Platform_Error.EBADRQC /* Invalid request code */ -EBADSLT :: Platform_Error.EBADSLT /* Invalid slot */ -EDEADLOCK :: Platform_Error.EDEADLOCK -EBFONT :: Platform_Error.EBFONT /* Bad font file format */ -ENOSTR :: Platform_Error.ENOSTR /* Device not a stream */ -ENODATA :: Platform_Error.ENODATA /* No data available */ -ETIME :: Platform_Error.ETIME /* Timer expired */ -ENOSR :: Platform_Error.ENOSR /* Out of streams resources */ -ENONET :: Platform_Error.ENONET /* Machine is not on the network */ -ENOPKG :: Platform_Error.ENOPKG /* Package not installed */ -EREMOTE :: Platform_Error.EREMOTE /* Object is remote */ -ENOLINK :: Platform_Error.ENOLINK /* Link has been severed */ -EADV :: Platform_Error.EADV /* Advertise error */ -ESRMNT :: Platform_Error.ESRMNT /* Srmount error */ -ECOMM :: Platform_Error.ECOMM /* Communication error on send */ -EPROTO :: Platform_Error.EPROTO /* Protocol error */ -EMULTIHOP :: Platform_Error.EMULTIHOP /* Multihop attempted */ -EDOTDOT :: Platform_Error.EDOTDOT /* RFS specific error */ -EBADMSG :: Platform_Error.EBADMSG /* Not a data message */ -EOVERFLOW :: Platform_Error.EOVERFLOW /* Value too large for defined data type */ -ENOTUNIQ :: Platform_Error.ENOTUNIQ /* Name not unique on network */ -EBADFD :: Platform_Error.EBADFD /* File descriptor in bad state */ -EREMCHG :: Platform_Error.EREMCHG /* Remote address changed */ -ELIBACC :: Platform_Error.ELIBACC /* Can not access a needed shared library */ -ELIBBAD :: Platform_Error.ELIBBAD /* Accessing a corrupted shared library */ -ELIBSCN :: Platform_Error.ELIBSCN /* .lib section in a.out corrupted */ -ELIBMAX :: Platform_Error.ELIBMAX /* Attempting to link in too many shared libraries */ -ELIBEXEC :: Platform_Error.ELIBEXEC /* Cannot exec a shared library directly */ -EILSEQ :: Platform_Error.EILSEQ /* Illegal byte sequence */ -ERESTART :: Platform_Error.ERESTART /* Interrupted system call should be restarted */ -ESTRPIPE :: Platform_Error.ESTRPIPE /* Streams pipe error */ -EUSERS :: Platform_Error.EUSERS /* Too many users */ -ENOTSOCK :: Platform_Error.ENOTSOCK /* Socket operation on non-socket */ -EDESTADDRREQ :: Platform_Error.EDESTADDRREQ /* Destination address required */ -EMSGSIZE :: Platform_Error.EMSGSIZE /* Message too long */ -EPROTOTYPE :: Platform_Error.EPROTOTYPE /* Protocol wrong type for socket */ -ENOPROTOOPT :: Platform_Error.ENOPROTOOPT /* Protocol not available */ -EPROTONOSUPPOR :: Platform_Error.EPROTONOSUPPORT /* Protocol not supported */ -ESOCKTNOSUPPOR :: Platform_Error.ESOCKTNOSUPPORT /* Socket type not supported */ -EOPNOTSUPP :: Platform_Error.EOPNOTSUPP /* Operation not supported on transport endpoint */ -EPFNOSUPPORT :: Platform_Error.EPFNOSUPPORT /* Protocol family not supported */ -EAFNOSUPPORT :: Platform_Error.EAFNOSUPPORT /* Address family not supported by protocol */ -EADDRINUSE :: Platform_Error.EADDRINUSE /* Address already in use */ -EADDRNOTAVAIL :: Platform_Error.EADDRNOTAVAIL /* Cannot assign requested address */ -ENETDOWN :: Platform_Error.ENETDOWN /* Network is down */ -ENETUNREACH :: Platform_Error.ENETUNREACH /* Network is unreachable */ -ENETRESET :: Platform_Error.ENETRESET /* Network dropped connection because of reset */ -ECONNABORTED :: Platform_Error.ECONNABORTED /* Software caused connection abort */ -ECONNRESET :: Platform_Error.ECONNRESET /* Connection reset by peer */ -ENOBUFS :: Platform_Error.ENOBUFS /* No buffer space available */ -EISCONN :: Platform_Error.EISCONN /* Transport endpoint is already connected */ -ENOTCONN :: Platform_Error.ENOTCONN /* Transport endpoint is not connected */ -ESHUTDOWN :: Platform_Error.ESHUTDOWN /* Cannot send after transport endpoint shutdown */ -ETOOMANYREFS :: Platform_Error.ETOOMANYREFS /* Too many references: cannot splice */ -ETIMEDOUT :: Platform_Error.ETIMEDOUT /* Connection timed out */ -ECONNREFUSED :: Platform_Error.ECONNREFUSED /* Connection refused */ -EHOSTDOWN :: Platform_Error.EHOSTDOWN /* Host is down */ -EHOSTUNREACH :: Platform_Error.EHOSTUNREACH /* No route to host */ -EALREADY :: Platform_Error.EALREADY /* Operation already in progress */ -EINPROGRESS :: Platform_Error.EINPROGRESS /* Operation now in progress */ -ESTALE :: Platform_Error.ESTALE /* Stale file handle */ -EUCLEAN :: Platform_Error.EUCLEAN /* Structure needs cleaning */ -ENOTNAM :: Platform_Error.ENOTNAM /* Not a XENIX named type file */ -ENAVAIL :: Platform_Error.ENAVAIL /* No XENIX semaphores available */ -EISNAM :: Platform_Error.EISNAM /* Is a named type file */ -EREMOTEIO :: Platform_Error.EREMOTEIO /* Remote I/O error */ -EDQUOT :: Platform_Error.EDQUOT /* Quota exceeded */ - -ENOMEDIUM :: Platform_Error.ENOMEDIUM /* No medium found */ -EMEDIUMTYPE :: Platform_Error.EMEDIUMTYPE /* Wrong medium type */ -ECANCELED :: Platform_Error.ECANCELED /* Operation Canceled */ -ENOKEY :: Platform_Error.ENOKEY /* Required key not available */ -EKEYEXPIRED :: Platform_Error.EKEYEXPIRED /* Key has expired */ -EKEYREVOKED :: Platform_Error.EKEYREVOKED /* Key has been revoked */ -EKEYREJECTED :: Platform_Error.EKEYREJECTED /* Key was rejected by service */ - -/* for robust mutexes */ -EOWNERDEAD :: Platform_Error.EOWNERDEAD /* Owner died */ -ENOTRECOVERABLE :: Platform_Error.ENOTRECOVERABLE /* State not recoverable */ - -ERFKILL :: Platform_Error.ERFKILL /* Operation not possible due to RF-kill */ - -EHWPOISON :: Platform_Error.EHWPOISON /* Memory page has hardware error */ - -ADDR_NO_RANDOMIZE :: 0x40000 - -O_RDONLY :: 0x00000 -O_WRONLY :: 0x00001 -O_RDWR :: 0x00002 -O_CREATE :: 0x00040 -O_EXCL :: 0x00080 -O_NOCTTY :: 0x00100 -O_TRUNC :: 0x00200 -O_NONBLOCK :: 0x00800 -O_APPEND :: 0x00400 -O_SYNC :: 0x01000 -O_ASYNC :: 0x02000 -O_CLOEXEC :: 0x80000 - - -SEEK_DATA :: 3 -SEEK_HOLE :: 4 -SEEK_MAX :: SEEK_HOLE - - -AF_UNSPEC: int : 0 -AF_UNIX: int : 1 -AF_LOCAL: int : AF_UNIX -AF_INET: int : 2 -AF_INET6: int : 10 -AF_PACKET: int : 17 -AF_BLUETOOTH: int : 31 - -SOCK_STREAM: int : 1 -SOCK_DGRAM: int : 2 -SOCK_RAW: int : 3 -SOCK_RDM: int : 4 -SOCK_SEQPACKET: int : 5 -SOCK_PACKET: int : 10 - -INADDR_ANY: c.ulong : 0 -INADDR_BROADCAST: c.ulong : 0xffffffff -INADDR_NONE: c.ulong : 0xffffffff -INADDR_DUMMY: c.ulong : 0xc0000008 - -IPPROTO_IP: int : 0 -IPPROTO_ICMP: int : 1 -IPPROTO_TCP: int : 6 -IPPROTO_UDP: int : 17 -IPPROTO_IPV6: int : 41 -IPPROTO_ETHERNET: int : 143 -IPPROTO_RAW: int : 255 - -SHUT_RD: int : 0 -SHUT_WR: int : 1 -SHUT_RDWR: int : 2 - - -SOL_SOCKET: int : 1 -SO_DEBUG: int : 1 -SO_REUSEADDR: int : 2 -SO_DONTROUTE: int : 5 -SO_BROADCAST: int : 6 -SO_SNDBUF: int : 7 -SO_RCVBUF: int : 8 -SO_KEEPALIVE: int : 9 -SO_OOBINLINE: int : 10 -SO_LINGER: int : 13 -SO_REUSEPORT: int : 15 -SO_RCVTIMEO_NEW: int : 66 -SO_SNDTIMEO_NEW: int : 67 - -TCP_NODELAY: int : 1 -TCP_CORK: int : 3 - -MSG_TRUNC : int : 0x20 - -// TODO: add remaining fcntl commands -// reference: https://github.com/torvalds/linux/blob/master/include/uapi/asm-generic/fcntl.h -F_GETFL: int : 3 /* Get file flags */ -F_SETFL: int : 4 /* Set file flags */ - -// NOTE(zangent): These are OS specific! -// Do not mix these up! -RTLD_LAZY :: 0x0001 -RTLD_NOW :: 0x0002 -RTLD_BINDING_MASK :: 0x0003 -RTLD_GLOBAL :: 0x0100 -RTLD_NOLOAD :: 0x0004 -RTLD_DEEPBIND :: 0x0008 -RTLD_NODELETE :: 0x1000 - -socklen_t :: c.int - -Timeval :: struct { - seconds: i64, - microseconds: int, -} - -// "Argv" arguments converted to Odin strings -args := _alloc_command_line_arguments() - -Unix_File_Time :: struct { - seconds: i64, - nanoseconds: i64, -} - -when ODIN_ARCH == .arm64 || ODIN_ARCH == .riscv64 { - OS_Stat :: struct { - device_id: u64, // ID of device containing file - serial: u64, // File serial number - mode: u32, // Mode of the file - nlink: u32, // Number of hard links - uid: u32, // User ID of the file's owner - gid: u32, // Group ID of the file's group - rdev: u64, // Device ID, if device - _: u64, // Padding - size: i64, // Size of the file, in bytes - block_size: i32, // Optimal blocksize for I/O - _: i32, // Padding - blocks: i64, // Number of 512-byte blocks allocated - - last_access: Unix_File_Time, // Time of last access - modified: Unix_File_Time, // Time of last modification - status_change: Unix_File_Time, // Time of last status change - - _reserved: [2]i32, - } - #assert(size_of(OS_Stat) == 128) -} else { - OS_Stat :: struct { - device_id: u64, // ID of device containing file - serial: u64, // File serial number - nlink: u64, // Number of hard links - mode: u32, // Mode of the file - uid: u32, // User ID of the file's owner - gid: u32, // Group ID of the file's group - _: i32, // 32 bits of padding - rdev: u64, // Device ID, if device - size: i64, // Size of the file, in bytes - block_size: i64, // Optimal bllocksize for I/O - blocks: i64, // Number of 512-byte blocks allocated - - last_access: Unix_File_Time, // Time of last access - modified: Unix_File_Time, // Time of last modification - status_change: Unix_File_Time, // Time of last status change - - _reserved: [3]i64, - } -} - -// NOTE(laleksic, 2021-01-21): Comment and rename these to match OS_Stat above -Dirent :: struct { - ino: u64, - off: u64, - reclen: u16, - type: u8, - name: [256]byte, -} - -ADDRESS_FAMILY :: u16 -SOCKADDR :: struct #packed { - sa_family: ADDRESS_FAMILY, - sa_data: [14]c.char, -} - -SOCKADDR_STORAGE_LH :: struct #packed { - ss_family: ADDRESS_FAMILY, - __ss_pad1: [6]c.char, - __ss_align: i64, - __ss_pad2: [112]c.char, -} - -sockaddr_in :: struct #packed { - sin_family: ADDRESS_FAMILY, - sin_port: u16be, - sin_addr: in_addr, - sin_zero: [8]c.char, -} - -sockaddr_in6 :: struct #packed { - sin6_family: ADDRESS_FAMILY, - sin6_port: u16be, - sin6_flowinfo: c.ulong, - sin6_addr: in6_addr, - sin6_scope_id: c.ulong, -} - -in_addr :: struct #packed { - s_addr: u32, -} - -in6_addr :: struct #packed { - s6_addr: [16]u8, -} - -rtnl_link_stats :: struct #packed { - rx_packets: u32, - tx_packets: u32, - rx_bytes: u32, - tx_bytes: u32, - rx_errors: u32, - tx_errors: u32, - rx_dropped: u32, - tx_dropped: u32, - multicast: u32, - collisions: u32, - rx_length_errors: u32, - rx_over_errors: u32, - rx_crc_errors: u32, - rx_frame_errors: u32, - rx_fifo_errors: u32, - rx_missed_errors: u32, - tx_aborted_errors: u32, - tx_carrier_errors: u32, - tx_fifo_errors: u32, - tx_heartbeat_errors: u32, - tx_window_errors: u32, - rx_compressed: u32, - tx_compressed: u32, - rx_nohandler: u32, -} - -SIOCGIFFLAG :: enum c.int { - UP = 0, /* Interface is up. */ - BROADCAST = 1, /* Broadcast address valid. */ - DEBUG = 2, /* Turn on debugging. */ - LOOPBACK = 3, /* Is a loopback net. */ - POINT_TO_POINT = 4, /* Interface is point-to-point link. */ - NO_TRAILERS = 5, /* Avoid use of trailers. */ - RUNNING = 6, /* Resources allocated. */ - NOARP = 7, /* No address resolution protocol. */ - PROMISC = 8, /* Receive all packets. */ - ALL_MULTI = 9, /* Receive all multicast packets. Unimplemented. */ - MASTER = 10, /* Master of a load balancer. */ - SLAVE = 11, /* Slave of a load balancer. */ - MULTICAST = 12, /* Supports multicast. */ - PORTSEL = 13, /* Can set media type. */ - AUTOMEDIA = 14, /* Auto media select active. */ - DYNAMIC = 15, /* Dialup device with changing addresses. */ - LOWER_UP = 16, - DORMANT = 17, - ECHO = 18, -} -SIOCGIFFLAGS :: bit_set[SIOCGIFFLAG; c.int] - -ifaddrs :: struct { - next: ^ifaddrs, - name: cstring, - flags: SIOCGIFFLAGS, - address: ^SOCKADDR, - netmask: ^SOCKADDR, - broadcast_or_dest: ^SOCKADDR, // Broadcast or Point-to-Point address - data: rawptr, // Address-specific data. -} - -Dir :: distinct rawptr // DIR* - -// File type -S_IFMT :: 0o170000 // Type of file mask -S_IFIFO :: 0o010000 // Named pipe (fifo) -S_IFCHR :: 0o020000 // Character special -S_IFDIR :: 0o040000 // Directory -S_IFBLK :: 0o060000 // Block special -S_IFREG :: 0o100000 // Regular -S_IFLNK :: 0o120000 // Symbolic link -S_IFSOCK :: 0o140000 // Socket - -// File mode -// Read, write, execute/search by owner -S_IRWXU :: 0o0700 // RWX mask for owner -S_IRUSR :: 0o0400 // R for owner -S_IWUSR :: 0o0200 // W for owner -S_IXUSR :: 0o0100 // X for owner - -// Read, write, execute/search by group -S_IRWXG :: 0o0070 // RWX mask for group -S_IRGRP :: 0o0040 // R for group -S_IWGRP :: 0o0020 // W for group -S_IXGRP :: 0o0010 // X for group - -// Read, write, execute/search by others -S_IRWXO :: 0o0007 // RWX mask for other -S_IROTH :: 0o0004 // R for other -S_IWOTH :: 0o0002 // W for other -S_IXOTH :: 0o0001 // X for other - -S_ISUID :: 0o4000 // Set user id on execution -S_ISGID :: 0o2000 // Set group id on execution -S_ISVTX :: 0o1000 // Directory restrcted delete - - -@(require_results) S_ISLNK :: #force_inline proc(m: u32) -> bool { return (m & S_IFMT) == S_IFLNK } -@(require_results) S_ISREG :: #force_inline proc(m: u32) -> bool { return (m & S_IFMT) == S_IFREG } -@(require_results) S_ISDIR :: #force_inline proc(m: u32) -> bool { return (m & S_IFMT) == S_IFDIR } -@(require_results) S_ISCHR :: #force_inline proc(m: u32) -> bool { return (m & S_IFMT) == S_IFCHR } -@(require_results) S_ISBLK :: #force_inline proc(m: u32) -> bool { return (m & S_IFMT) == S_IFBLK } -@(require_results) S_ISFIFO :: #force_inline proc(m: u32) -> bool { return (m & S_IFMT) == S_IFIFO } -@(require_results) S_ISSOCK :: #force_inline proc(m: u32) -> bool { return (m & S_IFMT) == S_IFSOCK } - -F_OK :: 0 // Test for file existance -X_OK :: 1 // Test for execute permission -W_OK :: 2 // Test for write permission -R_OK :: 4 // Test for read permission - -AT_FDCWD :: ~uintptr(99) /* -100 */ -AT_REMOVEDIR :: uintptr(0x200) -AT_SYMLINK_NOFOLLOW :: uintptr(0x100) - -pollfd :: struct { - fd: c.int, - events: c.short, - revents: c.short, -} - -sigset_t :: distinct u64 - -foreign libc { - @(link_name="__errno_location") __errno_location :: proc() -> ^c.int --- - - @(link_name="getpagesize") _unix_getpagesize :: proc() -> c.int --- - @(link_name="get_nprocs") _unix_get_nprocs :: proc() -> c.int --- - @(link_name="fdopendir") _unix_fdopendir :: proc(fd: Handle) -> Dir --- - @(link_name="closedir") _unix_closedir :: proc(dirp: Dir) -> c.int --- - @(link_name="rewinddir") _unix_rewinddir :: proc(dirp: Dir) --- - @(link_name="readdir_r") _unix_readdir_r :: proc(dirp: Dir, entry: ^Dirent, result: ^^Dirent) -> c.int --- - - @(link_name="malloc") _unix_malloc :: proc(size: c.size_t) -> rawptr --- - @(link_name="calloc") _unix_calloc :: proc(num, size: c.size_t) -> rawptr --- - @(link_name="free") _unix_free :: proc(ptr: rawptr) --- - @(link_name="realloc") _unix_realloc :: proc(ptr: rawptr, size: c.size_t) -> rawptr --- - - @(link_name="execvp") _unix_execvp :: proc(path: cstring, argv: [^]cstring) -> c.int --- - @(link_name="getenv") _unix_getenv :: proc(cstring) -> cstring --- - @(link_name="putenv") _unix_putenv :: proc(cstring) -> c.int --- - @(link_name="setenv") _unix_setenv :: proc(key: cstring, value: cstring, overwrite: c.int) -> c.int --- - @(link_name="realpath") _unix_realpath :: proc(path: cstring, resolved_path: [^]byte = nil) -> cstring --- - - @(link_name="exit") _unix_exit :: proc(status: c.int) -> ! --- -} -foreign dl { - @(link_name="dlopen") _unix_dlopen :: proc(filename: cstring, flags: c.int) -> rawptr --- - @(link_name="dlsym") _unix_dlsym :: proc(handle: rawptr, symbol: cstring) -> rawptr --- - @(link_name="dlclose") _unix_dlclose :: proc(handle: rawptr) -> c.int --- - @(link_name="dlerror") _unix_dlerror :: proc() -> cstring --- - - @(link_name="getifaddrs") _getifaddrs :: proc(ifap: ^^ifaddrs) -> (c.int) --- - @(link_name="freeifaddrs") _freeifaddrs :: proc(ifa: ^ifaddrs) --- -} - -@(require_results) -is_path_separator :: proc(r: rune) -> bool { - return r == '/' -} - -// determine errno from syscall return value -@(private, require_results) -_get_errno :: proc(res: int) -> Error { - if res < 0 && res > -4096 { - return Platform_Error(-res) - } - return nil -} - -// get errno from libc -@(require_results, no_instrumentation) -get_last_error :: proc "contextless" () -> Error { - err := Platform_Error(__errno_location()^) - #partial switch err { - case .NONE: - return nil - case .EPERM: - return .Permission_Denied - case .EEXIST: - return .Exist - case .ENOENT: - return .Not_Exist - } - return err -} - -personality :: proc(persona: u64) -> Error { - res := unix.sys_personality(persona) - if res == -1 { - return _get_errno(res) - } - return nil -} - -@(require_results) -fork :: proc() -> (Pid, Error) { - pid := unix.sys_fork() - if pid == -1 { - return -1, _get_errno(pid) - } - return Pid(pid), nil -} - -execvp :: proc(path: string, args: []string) -> Error { - runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD() - path_cstr := strings.clone_to_cstring(path, context.temp_allocator) - - args_cstrs := make([]cstring, len(args) + 2, context.temp_allocator) - args_cstrs[0] = strings.clone_to_cstring(path, context.temp_allocator) - for i := 0; i < len(args); i += 1 { - args_cstrs[i+1] = strings.clone_to_cstring(args[i], context.temp_allocator) - } - - _unix_execvp(path_cstr, raw_data(args_cstrs)) - return get_last_error() -} - - -@(require_results) -open :: proc(path: string, flags: int = O_RDONLY, mode: int = 0o000) -> (Handle, Error) { - runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD() - cstr := strings.clone_to_cstring(path, context.temp_allocator) - handle := unix.sys_open(cstr, flags, uint(mode)) - if handle < 0 { - return INVALID_HANDLE, _get_errno(handle) - } - return Handle(handle), nil -} - -close :: proc(fd: Handle) -> Error { - return _get_errno(unix.sys_close(int(fd))) -} - -flush :: proc(fd: Handle) -> Error { - return _get_errno(unix.sys_fsync(int(fd))) -} - -// If you read or write more than `SSIZE_MAX` bytes, result is implementation defined (probably an error). -// `SSIZE_MAX` is also implementation defined but usually the max of a `ssize_t` which is `max(int)` in Odin. -// In practice a read/write call would probably never read/write these big buffers all at once, -// which is why the number of bytes is returned and why there are procs that will call this in a -// loop for you. -// We set a max of 1GB to keep alignment and to be safe. -@(private) -MAX_RW :: 1 << 30 - -read :: proc(fd: Handle, data: []byte) -> (int, Error) { - if len(data) == 0 { - return 0, nil - } - - to_read := min(uint(len(data)), MAX_RW) - - bytes_read := unix.sys_read(int(fd), raw_data(data), to_read) - if bytes_read < 0 { - return -1, _get_errno(bytes_read) - } - return bytes_read, nil -} - -write :: proc(fd: Handle, data: []byte) -> (int, Error) { - if len(data) == 0 { - return 0, nil - } - - to_write := min(uint(len(data)), MAX_RW) - - bytes_written := unix.sys_write(int(fd), raw_data(data), to_write) - if bytes_written < 0 { - return -1, _get_errno(bytes_written) - } - return bytes_written, nil -} - -read_at :: proc(fd: Handle, data: []byte, offset: i64) -> (int, Error) { - if len(data) == 0 { - return 0, nil - } - - to_read := min(uint(len(data)), MAX_RW) - - bytes_read := unix.sys_pread(int(fd), raw_data(data), to_read, offset) - if bytes_read < 0 { - return -1, _get_errno(bytes_read) - } - return bytes_read, nil -} - -write_at :: proc(fd: Handle, data: []byte, offset: i64) -> (int, Error) { - if len(data) == 0 { - return 0, nil - } - - to_write := min(uint(len(data)), MAX_RW) - - bytes_written := unix.sys_pwrite(int(fd), raw_data(data), to_write, offset) - if bytes_written < 0 { - return -1, _get_errno(bytes_written) - } - return bytes_written, nil -} - -seek :: proc(fd: Handle, offset: i64, whence: int) -> (i64, Error) { - switch whence { - case SEEK_SET, SEEK_CUR, SEEK_END: - break - case: - return 0, .Invalid_Whence - } - res := unix.sys_lseek(int(fd), offset, whence) - if res < 0 { - errno := _get_errno(int(res)) - switch errno { - case .EINVAL: - return 0, .Invalid_Offset - } - return 0, errno - } - return i64(res), nil -} - -@(require_results, no_sanitize_memory) -file_size :: proc(fd: Handle) -> (i64, Error) { - // deliberately uninitialized; the syscall fills this buffer for us - s: OS_Stat = --- - result := unix.sys_fstat(int(fd), rawptr(&s)) - if result < 0 { - return 0, _get_errno(result) - } - return max(s.size, 0), nil -} - -rename :: proc(old_path, new_path: string) -> Error { - runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD() - old_path_cstr := strings.clone_to_cstring(old_path, context.temp_allocator) - new_path_cstr := strings.clone_to_cstring(new_path, context.temp_allocator) - return _get_errno(unix.sys_rename(old_path_cstr, new_path_cstr)) -} - -remove :: proc(path: string) -> Error { - runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD() - path_cstr := strings.clone_to_cstring(path, context.temp_allocator) - return _get_errno(unix.sys_unlink(path_cstr)) -} - -make_directory :: proc(path: string, mode: u32 = 0o775) -> Error { - runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD() - path_cstr := strings.clone_to_cstring(path, context.temp_allocator) - return _get_errno(unix.sys_mkdir(path_cstr, uint(mode))) -} - -remove_directory :: proc(path: string) -> Error { - runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD() - path_cstr := strings.clone_to_cstring(path, context.temp_allocator) - return _get_errno(unix.sys_rmdir(path_cstr)) -} - -@(require_results) -is_file_handle :: proc(fd: Handle) -> bool { - s, err := _fstat(fd) - if err != nil { - return false - } - return S_ISREG(s.mode) -} - -@(require_results) -is_file_path :: proc(path: string, follow_links: bool = true) -> bool { - s: OS_Stat - err: Error - if follow_links { - s, err = _stat(path) - } else { - s, err = _lstat(path) - } - if err != nil { - return false - } - return S_ISREG(s.mode) -} - - -@(require_results) -is_dir_handle :: proc(fd: Handle) -> bool { - s, err := _fstat(fd) - if err != nil { - return false - } - return S_ISDIR(s.mode) -} - -@(require_results) -is_dir_path :: proc(path: string, follow_links: bool = true) -> bool { - s: OS_Stat - err: Error - if follow_links { - s, err = _stat(path) - } else { - s, err = _lstat(path) - } - if err != nil { - return false - } - return S_ISDIR(s.mode) -} - -is_file :: proc {is_file_path, is_file_handle} -is_dir :: proc {is_dir_path, is_dir_handle} - -@(require_results) -exists :: proc(path: string) -> bool { - runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD() - cpath := strings.clone_to_cstring(path, context.temp_allocator) - res := unix.sys_access(cpath, O_RDONLY) - return res == 0 -} - -// NOTE(bill): Uses startup to initialize it - -stdin: Handle = 0 -stdout: Handle = 1 -stderr: Handle = 2 - -/* TODO(zangent): Implement these! -last_write_time :: proc(fd: Handle) -> File_Time {} -last_write_time_by_name :: proc(name: string) -> File_Time {} -*/ -@(require_results) -last_write_time :: proc(fd: Handle) -> (time: File_Time, err: Error) { - s := _fstat(fd) or_return - modified := s.modified.seconds * 1_000_000_000 + s.modified.nanoseconds - return File_Time(modified), nil -} - -@(require_results) -last_write_time_by_name :: proc(name: string) -> (time: File_Time, err: Error) { - s := _stat(name) or_return - modified := s.modified.seconds * 1_000_000_000 + s.modified.nanoseconds - return File_Time(modified), nil -} - -@(private, require_results, no_sanitize_memory) -_stat :: proc(path: string) -> (OS_Stat, Error) { - runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD() - cstr := strings.clone_to_cstring(path, context.temp_allocator) - - // deliberately uninitialized; the syscall fills this buffer for us - s: OS_Stat = --- - result := unix.sys_stat(cstr, &s) - if result < 0 { - return s, _get_errno(result) - } - return s, nil -} - -@(private, require_results, no_sanitize_memory) -_lstat :: proc(path: string) -> (OS_Stat, Error) { - runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD() - cstr := strings.clone_to_cstring(path, context.temp_allocator) - - // deliberately uninitialized; the syscall fills this buffer for us - s: OS_Stat = --- - result := unix.sys_lstat(cstr, &s) - if result < 0 { - return s, _get_errno(result) - } - return s, nil -} - -@(private, require_results, no_sanitize_memory) -_fstat :: proc(fd: Handle) -> (OS_Stat, Error) { - // deliberately uninitialized; the syscall fills this buffer for us - s: OS_Stat = --- - result := unix.sys_fstat(int(fd), rawptr(&s)) - if result < 0 { - return s, _get_errno(result) - } - return s, nil -} - -@(private, require_results) -_fdopendir :: proc(fd: Handle) -> (Dir, Error) { - dirp := _unix_fdopendir(fd) - if dirp == cast(Dir)nil { - return nil, get_last_error() - } - return dirp, nil -} - -@(private) -_closedir :: proc(dirp: Dir) -> Error { - rc := _unix_closedir(dirp) - if rc != 0 { - return get_last_error() - } - return nil -} - -@(private) -_rewinddir :: proc(dirp: Dir) { - _unix_rewinddir(dirp) -} - -@(private, require_results) -_readdir :: proc(dirp: Dir) -> (entry: Dirent, err: Error, end_of_stream: bool) { - result: ^Dirent - rc := _unix_readdir_r(dirp, &entry, &result) - - if rc != 0 { - err = get_last_error() - return - } - err = nil - - if result == nil { - end_of_stream = true - return - } - end_of_stream = false - - return -} - -@(private, require_results) -_readlink :: proc(path: string) -> (string, Error) { - runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD(ignore = context.temp_allocator == context.allocator) - path_cstr := strings.clone_to_cstring(path, context.temp_allocator) - - bufsz : uint = 256 - buf := make([]byte, bufsz) - for { - rc := unix.sys_readlink(path_cstr, &(buf[0]), bufsz) - if rc < 0 { - delete(buf) - return "", _get_errno(rc) - } else if rc == int(bufsz) { - // NOTE(laleksic, 2021-01-21): Any cleaner way to resize the slice? - bufsz *= 2 - delete(buf) - buf = make([]byte, bufsz) - } else { - return strings.string_from_ptr(&buf[0], rc), nil - } - } -} - -@(private, require_results) -_dup :: proc(fd: Handle) -> (Handle, Error) { - dup, err := linux.dup(linux.Fd(fd)) - return Handle(dup), err -} - -@(require_results) -absolute_path_from_handle :: proc(fd: Handle) -> (string, Error) { - buf : [256]byte - fd_str := strconv.write_int( buf[:], cast(i64)fd, 10 ) - - procfs_path := strings.concatenate( []string{ "/proc/self/fd/", fd_str } ) - defer delete(procfs_path) - - return _readlink(procfs_path) -} - -@(require_results) -absolute_path_from_relative :: proc(rel: string, allocator := context.allocator) -> (path: string, err: Error) { - rel := rel - if rel == "" { - rel = "." - } - runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD(ignore = context.temp_allocator == context.allocator) - - rel_cstr := strings.clone_to_cstring(rel, context.temp_allocator) - - path_ptr := _unix_realpath(rel_cstr, nil) - if path_ptr == nil { - return "", get_last_error() - } - defer _unix_free(rawptr(path_ptr)) - - return strings.clone(string(path_ptr), allocator) -} - -access :: proc(path: string, mask: int) -> (bool, Error) { - runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD() - cstr := strings.clone_to_cstring(path, context.temp_allocator) - result := unix.sys_access(cstr, mask) - if result < 0 { - return false, _get_errno(result) - } - return true, nil -} - -@(require_results) -lookup_env_alloc :: proc(key: string, allocator := context.allocator) -> (value: string, found: bool) { - runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD(ignore = context.temp_allocator == allocator) - path_str := strings.clone_to_cstring(key, context.temp_allocator) - // NOTE(tetra): Lifetime of 'cstr' is unclear, but _unix_free(cstr) segfaults. - cstr := _unix_getenv(path_str) - if cstr == nil { - return "", false - } - return strings.clone(string(cstr), allocator), true -} - -@(require_results) -lookup_env_buffer :: proc(buf: []u8, key: string) -> (value: string, err: Error) { - if len(key) + 1 > len(buf) { - return "", .Buffer_Full - } else { - copy(buf, key) - buf[len(key)] = 0 - } - - if value = string(_unix_getenv(cstring(raw_data(buf)))); value == "" { - return "", .Env_Var_Not_Found - } else { - if len(value) > len(buf) { - return "", .Buffer_Full - } else { - copy(buf, value) - return string(buf[:len(value)]), nil - } - } -} -lookup_env :: proc{lookup_env_alloc, lookup_env_buffer} - -@(require_results) -get_env_alloc :: proc(key: string, allocator := context.allocator) -> (value: string) { - value, _ = lookup_env(key, allocator) - return -} - -@(require_results) -get_env_buf :: proc(buf: []u8, key: string) -> (value: string) { - value, _ = lookup_env(buf, key) - return -} -get_env :: proc{get_env_alloc, get_env_buf} - -set_env :: proc(key, value: string) -> Error { - runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD() - key_cstring := strings.clone_to_cstring(key, context.temp_allocator) - value_cstring := strings.clone_to_cstring(value, context.temp_allocator) - // NOTE(GoNZooo): `setenv` instead of `putenv` because it copies both key and value more commonly - res := _unix_setenv(key_cstring, value_cstring, 1) - if res < 0 { - return get_last_error() - } - return nil -} - -unset_env :: proc(key: string) -> Error { - runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD() - s := strings.clone_to_cstring(key, context.temp_allocator) - res := _unix_putenv(s) - if res < 0 { - return get_last_error() - } - return nil -} - -@(require_results) -get_current_directory :: proc(allocator := context.allocator) -> string { - context.allocator = allocator - // NOTE(tetra): I would use PATH_MAX here, but I was not able to find - // an authoritative value for it across all systems. - // The largest value I could find was 4096, so might as well use the page size. - page_size := get_page_size() - buf := make([dynamic]u8, page_size) - for { - #no_bounds_check res := unix.sys_getcwd(&buf[0], uint(len(buf))) - - if res >= 0 { - return strings.string_from_null_terminated_ptr(&buf[0], len(buf)) - } - if _get_errno(res) != ERANGE { - delete(buf) - return "" - } - resize(&buf, len(buf)+page_size) - } - unreachable() -} - -set_current_directory :: proc(path: string) -> (err: Error) { - runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD() - cstr := strings.clone_to_cstring(path, context.temp_allocator) - res := unix.sys_chdir(cstr) - if res < 0 { - return _get_errno(res) - } - return nil -} - -exit :: proc "contextless" (code: int) -> ! { - runtime._cleanup_runtime_contextless() - _unix_exit(c.int(code)) -} - -@(require_results) -current_thread_id :: proc "contextless" () -> int { - return unix.sys_gettid() -} - -@(require_results) -dlopen :: proc(filename: string, flags: int) -> rawptr { - runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD() - cstr := strings.clone_to_cstring(filename, context.temp_allocator) - handle := _unix_dlopen(cstr, c.int(flags)) - return handle -} -@(require_results) -dlsym :: proc(handle: rawptr, symbol: string) -> rawptr { - assert(handle != nil) - runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD() - cstr := strings.clone_to_cstring(symbol, context.temp_allocator) - proc_handle := _unix_dlsym(handle, cstr) - return proc_handle -} -dlclose :: proc(handle: rawptr) -> bool { - assert(handle != nil) - return _unix_dlclose(handle) == 0 -} -dlerror :: proc() -> string { - return string(_unix_dlerror()) -} - -@(require_results) -get_page_size :: proc() -> int { - // NOTE(tetra): The page size never changes, so why do anything complicated - // if we don't have to. - @static page_size := -1 - if page_size != -1 { - return page_size - } - - page_size = int(_unix_getpagesize()) - return page_size -} - -@(private, require_results) -_processor_core_count :: proc() -> int { - return int(_unix_get_nprocs()) -} - -@(private, require_results) -_alloc_command_line_arguments :: proc "contextless" () -> []string { - context = runtime.default_context() - res := make([]string, len(runtime.args__)) - for _, i in res { - res[i] = string(runtime.args__[i]) - } - return res -} - -@(private, fini) -_delete_command_line_arguments :: proc "contextless" () { - context = runtime.default_context() - delete(args) -} - -@(require_results) -socket :: proc(domain: int, type: int, protocol: int) -> (Socket, Error) { - result := unix.sys_socket(domain, type, protocol) - if result < 0 { - return 0, _get_errno(result) - } - return Socket(result), nil -} - -bind :: proc(sd: Socket, addr: ^SOCKADDR, len: socklen_t) -> Error { - result := unix.sys_bind(int(sd), addr, len) - if result < 0 { - return _get_errno(result) - } - return nil -} - - -connect :: proc(sd: Socket, addr: ^SOCKADDR, len: socklen_t) -> Error { - result := unix.sys_connect(int(sd), addr, len) - if result < 0 { - return _get_errno(result) - } - return nil -} - -accept :: proc(sd: Socket, addr: ^SOCKADDR, len: rawptr) -> (Socket, Error) { - result := unix.sys_accept(int(sd), rawptr(addr), len) - if result < 0 { - return 0, _get_errno(result) - } - return Socket(result), nil -} - -listen :: proc(sd: Socket, backlog: int) -> Error { - result := unix.sys_listen(int(sd), backlog) - if result < 0 { - return _get_errno(result) - } - return nil -} - -setsockopt :: proc(sd: Socket, level: int, optname: int, optval: rawptr, optlen: socklen_t) -> Error { - result := unix.sys_setsockopt(int(sd), level, optname, optval, optlen) - if result < 0 { - return _get_errno(result) - } - return nil -} - - -recvfrom :: proc(sd: Socket, data: []byte, flags: int, addr: ^SOCKADDR, addr_size: ^socklen_t) -> (u32, Error) { - result := unix.sys_recvfrom(int(sd), raw_data(data), len(data), flags, addr, uintptr(addr_size)) - if result < 0 { - return 0, _get_errno(int(result)) - } - return u32(result), nil -} - -recv :: proc(sd: Socket, data: []byte, flags: int) -> (u32, Error) { - result := unix.sys_recvfrom(int(sd), raw_data(data), len(data), flags, nil, 0) - if result < 0 { - return 0, _get_errno(int(result)) - } - return u32(result), nil -} - - -sendto :: proc(sd: Socket, data: []u8, flags: int, addr: ^SOCKADDR, addrlen: socklen_t) -> (u32, Error) { - result := unix.sys_sendto(int(sd), raw_data(data), len(data), flags, addr, addrlen) - if result < 0 { - return 0, _get_errno(int(result)) - } - return u32(result), nil -} - -send :: proc(sd: Socket, data: []byte, flags: int) -> (u32, Error) { - result := unix.sys_sendto(int(sd), raw_data(data), len(data), flags, nil, 0) - if result < 0 { - return 0, _get_errno(int(result)) - } - return u32(result), nil -} - -shutdown :: proc(sd: Socket, how: int) -> Error { - result := unix.sys_shutdown(int(sd), how) - if result < 0 { - return _get_errno(result) - } - return nil -} - -fcntl :: proc(fd: int, cmd: int, arg: int) -> (int, Error) { - result := unix.sys_fcntl(fd, cmd, arg) - if result < 0 { - return 0, _get_errno(result) - } - return result, nil -} - -@(require_results) -poll :: proc(fds: []pollfd, timeout: int) -> (int, Error) { - result := unix.sys_poll(raw_data(fds), uint(len(fds)), timeout) - if result < 0 { - return 0, _get_errno(result) - } - return result, nil -} - -@(require_results) -ppoll :: proc(fds: []pollfd, timeout: ^unix.timespec, sigmask: ^sigset_t) -> (int, Error) { - result := unix.sys_ppoll(raw_data(fds), uint(len(fds)), timeout, sigmask, size_of(sigset_t)) - if result < 0 { - return 0, _get_errno(result) - } - return result, nil -} diff --git a/core/os/old/os_netbsd.odin b/core/os/old/os_netbsd.odin deleted file mode 100644 index 601e42199..000000000 --- a/core/os/old/os_netbsd.odin +++ /dev/null @@ -1,1032 +0,0 @@ -package os_old - -foreign import dl "system:dl" -foreign import libc "system:c" - -import "base:runtime" -import "core:strings" -import "core:c" - -Handle :: distinct i32 -File_Time :: distinct u64 - -INVALID_HANDLE :: ~Handle(0) - -_Platform_Error :: enum i32 { - NONE = 0, - EPERM = 1, /* Operation not permitted */ - ENOENT = 2, /* No such file or directory */ - EINTR = 4, /* Interrupted system call */ - ESRCH = 3, /* No such process */ - EIO = 5, /* Input/output error */ - ENXIO = 6, /* Device not configured */ - E2BIG = 7, /* Argument list too long */ - ENOEXEC = 8, /* Exec format error */ - EBADF = 9, /* Bad file descriptor */ - ECHILD = 10, /* No child processes */ - EDEADLK = 11, /* Resource deadlock avoided. 11 was EAGAIN */ - ENOMEM = 12, /* Cannot allocate memory */ - EACCES = 13, /* Permission denied */ - EFAULT = 14, /* Bad address */ - ENOTBLK = 15, /* Block device required */ - EBUSY = 16, /* Device busy */ - EEXIST = 17, /* File exists */ - EXDEV = 18, /* Cross-device link */ - ENODEV = 19, /* Operation not supported by device */ - ENOTDIR = 20, /* Not a directory */ - EISDIR = 21, /* Is a directory */ - EINVAL = 22, /* Invalid argument */ - ENFILE = 23, /* Too many open files in system */ - EMFILE = 24, /* Too many open files */ - ENOTTY = 25, /* Inappropriate ioctl for device */ - ETXTBSY = 26, /* Text file busy */ - EFBIG = 27, /* File too large */ - ENOSPC = 28, /* No space left on device */ - ESPIPE = 29, /* Illegal seek */ - EROFS = 30, /* Read-only file system */ - EMLINK = 31, /* Too many links */ - EPIPE = 32, /* Broken pipe */ - - /* math software */ - EDOM = 33, /* Numerical argument out of domain */ - ERANGE = 34, /* Result too large or too small */ - - /* non-blocking and interrupt i/o */ - EAGAIN = 35, /* Resource temporarily unavailable */ - EWOULDBLOCK = EAGAIN, /* Operation would block */ - EINPROGRESS = 36, /* Operation now in progress */ - EALREADY = 37, /* Operation already in progress */ - - /* ipc/network software -- argument errors */ - ENOTSOCK = 38, /* Socket operation on non-socket */ - EDESTADDRREQ = 39, /* Destination address required */ - EMSGSIZE = 40, /* Message too long */ - EPROTOTYPE = 41, /* Protocol wrong type for socket */ - ENOPROTOOPT = 42, /* Protocol option not available */ - EPROTONOSUPPORT = 43, /* Protocol not supported */ - ESOCKTNOSUPPORT = 44, /* Socket type not supported */ - EOPNOTSUPP = 45, /* Operation not supported */ - EPFNOSUPPORT = 46, /* Protocol family not supported */ - EAFNOSUPPORT = 47, /* Address family not supported by protocol family */ - EADDRINUSE = 48, /* Address already in use */ - EADDRNOTAVAIL = 49, /* Can't assign requested address */ - - /* ipc/network software -- operational errors */ - ENETDOWN = 50, /* Network is down */ - ENETUNREACH = 51, /* Network is unreachable */ - ENETRESET = 52, /* Network dropped connection on reset */ - ECONNABORTED = 53, /* Software caused connection abort */ - ECONNRESET = 54, /* Connection reset by peer */ - ENOBUFS = 55, /* No buffer space available */ - EISCONN = 56, /* Socket is already connected */ - ENOTCONN = 57, /* Socket is not connected */ - ESHUTDOWN = 58, /* Can't send after socket shutdown */ - ETOOMANYREFS = 59, /* Too many references: can't splice */ - ETIMEDOUT = 60, /* Operation timed out */ - ECONNREFUSED = 61, /* Connection refused */ - - ELOOP = 62, /* Too many levels of symbolic links */ - ENAMETOOLONG = 63, /* File name too long */ - - /* should be rearranged */ - EHOSTDOWN = 64, /* Host is down */ - EHOSTUNREACH = 65, /* No route to host */ - ENOTEMPTY = 66, /* Directory not empty */ - - /* quotas & mush */ - EPROCLIM = 67, /* Too many processes */ - EUSERS = 68, /* Too many users */ - EDQUOT = 69, /* Disc quota exceeded */ - - /* Network File System */ - ESTALE = 70, /* Stale NFS file handle */ - EREMOTE = 71, /* Too many levels of remote in path */ - EBADRPC = 72, /* RPC struct is bad */ - ERPCMISMATCH = 73, /* RPC version wrong */ - EPROGUNAVAIL = 74, /* RPC prog. not avail */ - EPROGMISMATCH = 75, /* Program version wrong */ - EPROCUNAVAIL = 76, /* Bad procedure for program */ - - ENOLCK = 77, /* No locks available */ - ENOSYS = 78, /* Function not implemented */ - - EFTYPE = 79, /* Inappropriate file type or format */ - EAUTH = 80, /* Authentication error */ - ENEEDAUTH = 81, /* Need authenticator */ - - /* SystemV IPC */ - EIDRM = 82, /* Identifier removed */ - ENOMSG = 83, /* No message of desired type */ - EOVERFLOW = 84, /* Value too large to be stored in data type */ - - /* Wide/multibyte-character handling, ISO/IEC 9899/AMD1:1995 */ - EILSEQ = 85, /* Illegal byte sequence */ - - /* From IEEE Std 1003.1-2001 */ - /* Base, Realtime, Threads or Thread Priority Scheduling option errors */ - ENOTSUP = 86, /* Not supported */ - - /* Realtime option errors */ - ECANCELED = 87, /* Operation canceled */ - - /* Realtime, XSI STREAMS option errors */ - EBADMSG = 88, /* Bad or Corrupt message */ - - /* XSI STREAMS option errors */ - ENODATA = 89, /* No message available */ - ENOSR = 90, /* No STREAM resources */ - ENOSTR = 91, /* Not a STREAM */ - ETIME = 92, /* STREAM ioctl timeout */ - - /* File system extended attribute errors */ - ENOATTR = 93, /* Attribute not found */ - - /* Realtime, XSI STREAMS option errors */ - EMULTIHOP = 94, /* Multihop attempted */ - ENOLINK = 95, /* Link has been severed */ - EPROTO = 96, /* Protocol error */ - - /* Robust mutexes */ - EOWNERDEAD = 97, /* Previous owner died */ - ENOTRECOVERABLE = 98, /* State not recoverable */ - - ELAST = 98, /* Must equal largest Error */ -} - -EPERM :: Platform_Error.EPERM /* Operation not permitted */ -ENOENT :: Platform_Error.ENOENT /* No such file or directory */ -EINTR :: Platform_Error.EINTR /* Interrupted system call */ -ESRCH :: Platform_Error.ESRCH /* No such process */ -EIO :: Platform_Error.EIO /* Input/output error */ -ENXIO :: Platform_Error.ENXIO /* Device not configured */ -E2BIG :: Platform_Error.E2BIG /* Argument list too long */ -ENOEXEC :: Platform_Error.ENOEXEC /* Exec format error */ -EBADF :: Platform_Error.EBADF /* Bad file descriptor */ -ECHILD :: Platform_Error.ECHILD /* No child processes */ -EDEADLK :: Platform_Error.EDEADLK /* Resource deadlock avoided. 11 was EAGAIN */ -ENOMEM :: Platform_Error.ENOMEM /* Cannot allocate memory */ -EACCES :: Platform_Error.EACCES /* Permission denied */ -EFAULT :: Platform_Error.EFAULT /* Bad address */ -ENOTBLK :: Platform_Error.ENOTBLK /* Block device required */ -EBUSY :: Platform_Error.EBUSY /* Device busy */ -EEXIST :: Platform_Error.EEXIST /* File exists */ -EXDEV :: Platform_Error.EXDEV /* Cross-device link */ -ENODEV :: Platform_Error.ENODEV /* Operation not supported by device */ -ENOTDIR :: Platform_Error.ENOTDIR /* Not a directory */ -EISDIR :: Platform_Error.EISDIR /* Is a directory */ -EINVAL :: Platform_Error.EINVAL /* Invalid argument */ -ENFILE :: Platform_Error.ENFILE /* Too many open files in system */ -EMFILE :: Platform_Error.EMFILE /* Too many open files */ -ENOTTY :: Platform_Error.ENOTTY /* Inappropriate ioctl for device */ -ETXTBSY :: Platform_Error.ETXTBSY /* Text file busy */ -EFBIG :: Platform_Error.EFBIG /* File too large */ -ENOSPC :: Platform_Error.ENOSPC /* No space left on device */ -ESPIPE :: Platform_Error.ESPIPE /* Illegal seek */ -EROFS :: Platform_Error.EROFS /* Read-only file system */ -EMLINK :: Platform_Error.EMLINK /* Too many links */ -EPIPE :: Platform_Error.EPIPE /* Broken pipe */ - -/* math software */ -EDOM :: Platform_Error.EDOM /* Numerical argument out of domain */ -ERANGE :: Platform_Error.ERANGE /* Result too large or too small */ - -/* non-blocking and interrupt i/o */ -EAGAIN :: Platform_Error.EAGAIN /* Resource temporarily unavailable */ -EWOULDBLOCK :: EAGAIN /* Operation would block */ -EINPROGRESS :: Platform_Error.EINPROGRESS /* Operation now in progress */ -EALREADY :: Platform_Error.EALREADY /* Operation already in progress */ - -/* ipc/network software -- argument errors */ -ENOTSOCK :: Platform_Error.ENOTSOCK /* Socket operation on non-socket */ -EDESTADDRREQ :: Platform_Error.EDESTADDRREQ /* Destination address required */ -EMSGSIZE :: Platform_Error.EMSGSIZE /* Message too long */ -EPROTOTYPE :: Platform_Error.EPROTOTYPE /* Protocol wrong type for socket */ -ENOPROTOOPT :: Platform_Error.ENOPROTOOPT /* Protocol option not available */ -EPROTONOSUPPORT :: Platform_Error.EPROTONOSUPPORT /* Protocol not supported */ -ESOCKTNOSUPPORT :: Platform_Error.ESOCKTNOSUPPORT /* Socket type not supported */ -EOPNOTSUPP :: Platform_Error.EOPNOTSUPP /* Operation not supported */ -EPFNOSUPPORT :: Platform_Error.EPFNOSUPPORT /* Protocol family not supported */ -EAFNOSUPPORT :: Platform_Error.EAFNOSUPPORT /* Address family not supported by protocol family */ -EADDRINUSE :: Platform_Error.EADDRINUSE /* Address already in use */ -EADDRNOTAVAIL :: Platform_Error.EADDRNOTAVAIL /* Can't assign requested address */ - -/* ipc/network software -- operational errors */ -ENETDOWN :: Platform_Error.ENETDOWN /* Network is down */ -ENETUNREACH :: Platform_Error.ENETUNREACH /* Network is unreachable */ -ENETRESET :: Platform_Error.ENETRESET /* Network dropped connection on reset */ -ECONNABORTED :: Platform_Error.ECONNABORTED /* Software caused connection abort */ -ECONNRESET :: Platform_Error.ECONNRESET /* Connection reset by peer */ -ENOBUFS :: Platform_Error.ENOBUFS /* No buffer space available */ -EISCONN :: Platform_Error.EISCONN /* Socket is already connected */ -ENOTCONN :: Platform_Error.ENOTCONN /* Socket is not connected */ -ESHUTDOWN :: Platform_Error.ESHUTDOWN /* Can't send after socket shutdown */ -ETOOMANYREFS :: Platform_Error.ETOOMANYREFS /* Too many references: can't splice */ -ETIMEDOUT :: Platform_Error.ETIMEDOUT /* Operation timed out */ -ECONNREFUSED :: Platform_Error.ECONNREFUSED /* Connection refused */ - -ELOOP :: Platform_Error.ELOOP /* Too many levels of symbolic links */ -ENAMETOOLONG :: Platform_Error.ENAMETOOLONG /* File name too long */ - -/* should be rearranged */ -EHOSTDOWN :: Platform_Error.EHOSTDOWN /* Host is down */ -EHOSTUNREACH :: Platform_Error.EHOSTUNREACH /* No route to host */ -ENOTEMPTY :: Platform_Error.ENOTEMPTY /* Directory not empty */ - -/* quotas & mush */ -EPROCLIM :: Platform_Error.EPROCLIM /* Too many processes */ -EUSERS :: Platform_Error.EUSERS /* Too many users */ -EDQUOT :: Platform_Error.EDQUOT /* Disc quota exceeded */ - -/* Network File System */ -ESTALE :: Platform_Error.ESTALE /* Stale NFS file handle */ -EREMOTE :: Platform_Error.EREMOTE /* Too many levels of remote in path */ -EBADRPC :: Platform_Error.EBADRPC /* RPC struct is bad */ -ERPCMISMATCH :: Platform_Error.ERPCMISMATCH /* RPC version wrong */ -EPROGUNAVAIL :: Platform_Error.EPROGUNAVAIL /* RPC prog. not avail */ -EPROGMISMATCH :: Platform_Error.EPROGMISMATCH /* Program version wrong */ -EPROCUNAVAIL :: Platform_Error.EPROCUNAVAIL /* Bad procedure for program */ - -ENOLCK :: Platform_Error.ENOLCK /* No locks available */ -ENOSYS :: Platform_Error.ENOSYS /* Function not implemented */ - -EFTYPE :: Platform_Error.EFTYPE /* Inappropriate file type or format */ -EAUTH :: Platform_Error.EAUTH /* Authentication error */ -ENEEDAUTH :: Platform_Error.ENEEDAUTH /* Need authenticator */ - -/* SystemV IPC */ -EIDRM :: Platform_Error.EIDRM /* Identifier removed */ -ENOMSG :: Platform_Error.ENOMSG /* No message of desired type */ -EOVERFLOW :: Platform_Error.EOVERFLOW /* Value too large to be stored in data type */ - -/* Wide/multibyte-character handling, ISO/IEC 9899/AMD1:1995 */ -EILSEQ :: Platform_Error.EILSEQ /* Illegal byte sequence */ - -/* From IEEE Std 1003.1-2001 */ -/* Base, Realtime, Threads or Thread Priority Scheduling option errors */ -ENOTSUP :: Platform_Error.ENOTSUP /* Not supported */ - -/* Realtime option errors */ -ECANCELED :: Platform_Error.ECANCELED /* Operation canceled */ - -/* Realtime, XSI STREAMS option errors */ -EBADMSG :: Platform_Error.EBADMSG /* Bad or Corrupt message */ - -/* XSI STREAMS option errors */ -ENODATA :: Platform_Error.ENODATA /* No message available */ -ENOSR :: Platform_Error.ENOSR /* No STREAM resources */ -ENOSTR :: Platform_Error.ENOSTR /* Not a STREAM */ -ETIME :: Platform_Error.ETIME /* STREAM ioctl timeout */ - -/* File system extended attribute errors */ -ENOATTR :: Platform_Error.ENOATTR /* Attribute not found */ - -/* Realtime, XSI STREAMS option errors */ -EMULTIHOP :: Platform_Error.EMULTIHOP /* Multihop attempted */ -ENOLINK :: Platform_Error.ENOLINK /* Link has been severed */ -EPROTO :: Platform_Error.EPROTO /* Protocol error */ - -/* Robust mutexes */ -EOWNERDEAD :: Platform_Error.EOWNERDEAD /* Previous owner died */ -ENOTRECOVERABLE :: Platform_Error.ENOTRECOVERABLE /* State not recoverable */ - -ELAST :: Platform_Error.ELAST /* Must equal largest Error */ - -/* end of Error */ - -O_RDONLY :: 0x000000000 -O_WRONLY :: 0x000000001 -O_RDWR :: 0x000000002 -O_CREATE :: 0x000000200 -O_EXCL :: 0x000000800 -O_NOCTTY :: 0x000008000 -O_TRUNC :: 0x000000400 -O_NONBLOCK :: 0x000000004 -O_APPEND :: 0x000000008 -O_SYNC :: 0x000000080 -O_ASYNC :: 0x000000040 -O_CLOEXEC :: 0x000400000 - -RTLD_LAZY :: 0x001 -RTLD_NOW :: 0x002 -RTLD_GLOBAL :: 0x100 -RTLD_LOCAL :: 0x200 -RTLD_TRACE :: 0x200 -RTLD_NODELETE :: 0x01000 -RTLD_NOLOAD :: 0x02000 - -F_GETPATH :: 15 - -MAX_PATH :: 1024 -MAXNAMLEN :: 511 - -args := _alloc_command_line_arguments() - -Unix_File_Time :: struct { - seconds: time_t, - nanoseconds: c.long, -} - -dev_t :: u64 -ino_t :: u64 -nlink_t :: u32 -off_t :: i64 -mode_t :: u32 -pid_t :: u32 -uid_t :: u32 -gid_t :: u32 -blkcnt_t :: i64 -blksize_t :: i32 -fflags_t :: u32 -time_t :: i64 - -OS_Stat :: struct { - device_id: dev_t, - mode: mode_t, - _padding0: i16, - ino: ino_t, - nlink: nlink_t, - uid: uid_t, - gid: gid_t, - _padding1: i32, - rdev: dev_t, - - last_access: Unix_File_Time, - modified: Unix_File_Time, - status_change: Unix_File_Time, - birthtime: Unix_File_Time, - - size: off_t, - blocks: blkcnt_t, - block_size: blksize_t, - - flags: fflags_t, - gen: u32, - lspare: [2]u32, -} - -Dirent :: struct { - ino: ino_t, - reclen: u16, - namlen: u16, - type: u8, - name: [MAXNAMLEN + 1]byte, -} - -Dir :: distinct rawptr // DIR* - -// File type -S_IFMT :: 0o170000 // Type of file mask -S_IFIFO :: 0o010000 // Named pipe (fifo) -S_IFCHR :: 0o020000 // Character special -S_IFDIR :: 0o040000 // Directory -S_IFBLK :: 0o060000 // Block special -S_IFREG :: 0o100000 // Regular -S_IFLNK :: 0o120000 // Symbolic link -S_IFSOCK :: 0o140000 // Socket - -// File mode -// Read, write, execute/search by owner -S_IRWXU :: 0o0700 // RWX mask for owner -S_IRUSR :: 0o0400 // R for owner -S_IWUSR :: 0o0200 // W for owner -S_IXUSR :: 0o0100 // X for owner - -// Read, write, execute/search by group -S_IRWXG :: 0o0070 // RWX mask for group -S_IRGRP :: 0o0040 // R for group -S_IWGRP :: 0o0020 // W for group -S_IXGRP :: 0o0010 // X for group - -// Read, write, execute/search by others -S_IRWXO :: 0o0007 // RWX mask for other -S_IROTH :: 0o0004 // R for other -S_IWOTH :: 0o0002 // W for other -S_IXOTH :: 0o0001 // X for other - -S_ISUID :: 0o4000 // Set user id on execution -S_ISGID :: 0o2000 // Set group id on execution -S_ISVTX :: 0o1000 // Directory restrcted delete - -@(require_results) S_ISLNK :: #force_inline proc "contextless" (m: mode_t) -> bool { return (m & S_IFMT) == S_IFLNK } -@(require_results) S_ISREG :: #force_inline proc "contextless" (m: mode_t) -> bool { return (m & S_IFMT) == S_IFREG } -@(require_results) S_ISDIR :: #force_inline proc "contextless" (m: mode_t) -> bool { return (m & S_IFMT) == S_IFDIR } -@(require_results) S_ISCHR :: #force_inline proc "contextless" (m: mode_t) -> bool { return (m & S_IFMT) == S_IFCHR } -@(require_results) S_ISBLK :: #force_inline proc "contextless" (m: mode_t) -> bool { return (m & S_IFMT) == S_IFBLK } -@(require_results) S_ISFIFO :: #force_inline proc "contextless" (m: mode_t) -> bool { return (m & S_IFMT) == S_IFIFO } -@(require_results) S_ISSOCK :: #force_inline proc "contextless" (m: mode_t) -> bool { return (m & S_IFMT) == S_IFSOCK } - -F_OK :: 0 // Test for file existance -X_OK :: 1 // Test for execute permission -W_OK :: 2 // Test for write permission -R_OK :: 4 // Test for read permission - -foreign libc { - @(link_name="__errno") __errno_location :: proc() -> ^c.int --- - - @(link_name="open") _unix_open :: proc(path: cstring, flags: c.int, #c_vararg mode: ..u32) -> Handle --- - @(link_name="close") _unix_close :: proc(fd: Handle) -> c.int --- - @(link_name="read") _unix_read :: proc(fd: Handle, buf: rawptr, size: c.size_t) -> c.ssize_t --- - @(link_name="pread") _unix_pread :: proc(fd: Handle, buf: rawptr, size: c.size_t, offset: i64) -> c.ssize_t --- - @(link_name="write") _unix_write :: proc(fd: Handle, buf: rawptr, size: c.size_t) -> c.ssize_t --- - @(link_name="pwrite") _unix_pwrite :: proc(fd: Handle, buf: rawptr, size: c.size_t, offset: i64) -> c.ssize_t --- - @(link_name="lseek") _unix_seek :: proc(fd: Handle, offset: i64, whence: c.int) -> i64 --- - @(link_name="getpagesize") _unix_getpagesize :: proc() -> c.int --- - @(link_name="stat") _unix_stat :: proc(path: cstring, stat: ^OS_Stat) -> c.int --- - @(link_name="__lstat50") _unix_lstat :: proc(path: cstring, sb: ^OS_Stat) -> c.int --- - @(link_name="__fstat50") _unix_fstat :: proc(fd: Handle, stat: ^OS_Stat) -> c.int --- - @(link_name="readlink") _unix_readlink :: proc(path: cstring, buf: ^byte, bufsiz: c.size_t) -> c.ssize_t --- - @(link_name="access") _unix_access :: proc(path: cstring, mask: c.int) -> c.int --- - @(link_name="getcwd") _unix_getcwd :: proc(buf: cstring, len: c.size_t) -> cstring --- - @(link_name="chdir") _unix_chdir :: proc(buf: cstring) -> c.int --- - @(link_name="rename") _unix_rename :: proc(old, new: cstring) -> c.int --- - @(link_name="unlink") _unix_unlink :: proc(path: cstring) -> c.int --- - @(link_name="rmdir") _unix_rmdir :: proc(path: cstring) -> c.int --- - @(link_name="mkdir") _unix_mkdir :: proc(path: cstring, mode: mode_t) -> c.int --- - @(link_name="fcntl") _unix_fcntl :: proc(fd: Handle, cmd: c.int, #c_vararg args: ..any) -> c.int --- - @(link_name="fsync") _unix_fsync :: proc(fd: Handle) -> c.int --- - @(link_name="dup") _unix_dup :: proc(fd: Handle) -> Handle --- - - @(link_name="fdopendir") _unix_fdopendir :: proc(fd: Handle) -> Dir --- - @(link_name="closedir") _unix_closedir :: proc(dirp: Dir) -> c.int --- - @(link_name="rewinddir") _unix_rewinddir :: proc(dirp: Dir) --- - @(link_name="__readdir_r30") _unix_readdir_r :: proc(dirp: Dir, entry: ^Dirent, result: ^^Dirent) -> c.int --- - - @(link_name="malloc") _unix_malloc :: proc(size: c.size_t) -> rawptr --- - @(link_name="calloc") _unix_calloc :: proc(num, size: c.size_t) -> rawptr --- - @(link_name="free") _unix_free :: proc(ptr: rawptr) --- - @(link_name="realloc") _unix_realloc :: proc(ptr: rawptr, size: c.size_t) -> rawptr --- - - @(link_name="getenv") _unix_getenv :: proc(cstring) -> cstring --- - @(link_name="realpath") _unix_realpath :: proc(path: cstring, resolved_path: [^]byte = nil) -> cstring --- - @(link_name="sysctlbyname") _sysctlbyname :: proc(path: cstring, oldp: rawptr, oldlenp: rawptr, newp: rawptr, newlen: int) -> c.int --- - - @(link_name="exit") _unix_exit :: proc(status: c.int) -> ! --- -} - -foreign dl { - @(link_name="dlopen") _unix_dlopen :: proc(filename: cstring, flags: c.int) -> rawptr --- - @(link_name="dlsym") _unix_dlsym :: proc(handle: rawptr, symbol: cstring) -> rawptr --- - @(link_name="dlclose") _unix_dlclose :: proc(handle: rawptr) -> c.int --- - @(link_name="dlerror") _unix_dlerror :: proc() -> cstring --- -} - -@(private) -foreign libc { - _lwp_self :: proc() -> i32 --- -} - -// NOTE(phix): Perhaps share the following functions with FreeBSD if they turn out to be the same in the end. - -@(require_results) -is_path_separator :: proc(r: rune) -> bool { - return r == '/' -} - -@(require_results, no_instrumentation) -get_last_error :: proc "contextless" () -> Error { - return Platform_Error(__errno_location()^) -} - -@(require_results) -open :: proc(path: string, flags: int = O_RDONLY, mode: int = 0) -> (Handle, Error) { - runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD() - cstr := strings.clone_to_cstring(path, context.temp_allocator) - handle := _unix_open(cstr, c.int(flags), c.uint(mode)) - if handle == -1 { - return INVALID_HANDLE, get_last_error() - } - return handle, nil -} - -close :: proc(fd: Handle) -> Error { - result := _unix_close(fd) - if result == -1 { - return get_last_error() - } - return nil -} - -flush :: proc(fd: Handle) -> Error { - result := _unix_fsync(fd) - if result == -1 { - return get_last_error() - } - return nil -} - -// We set a max of 1GB to keep alignment and to be safe. -@(private) -MAX_RW :: 1 << 30 - -read :: proc(fd: Handle, data: []byte) -> (int, Error) { - to_read := min(c.size_t(len(data)), MAX_RW) - bytes_read := _unix_read(fd, &data[0], to_read) - if bytes_read == -1 { - return -1, get_last_error() - } - return int(bytes_read), nil -} - -write :: proc(fd: Handle, data: []byte) -> (int, Error) { - if len(data) == 0 { - return 0, nil - } - - to_write := min(c.size_t(len(data)), MAX_RW) - bytes_written := _unix_write(fd, &data[0], to_write) - if bytes_written == -1 { - return -1, get_last_error() - } - return int(bytes_written), nil -} - -read_at :: proc(fd: Handle, data: []byte, offset: i64) -> (n: int, err: Error) { - if len(data) == 0 { - return 0, nil - } - - to_read := min(uint(len(data)), MAX_RW) - - bytes_read := _unix_pread(fd, raw_data(data), to_read, offset) - if bytes_read < 0 { - return -1, get_last_error() - } - return bytes_read, nil -} - -write_at :: proc(fd: Handle, data: []byte, offset: i64) -> (n: int, err: Error) { - if len(data) == 0 { - return 0, nil - } - - to_write := min(uint(len(data)), MAX_RW) - - bytes_written := _unix_pwrite(fd, raw_data(data), to_write, offset) - if bytes_written < 0 { - return -1, get_last_error() - } - return bytes_written, nil -} - -seek :: proc(fd: Handle, offset: i64, whence: int) -> (i64, Error) { - switch whence { - case SEEK_SET, SEEK_CUR, SEEK_END: - break - case: - return 0, .Invalid_Whence - } - res := _unix_seek(fd, offset, c.int(whence)) - if res == -1 { - errno := get_last_error() - switch errno { - case .EINVAL: - return 0, .Invalid_Offset - } - return 0, errno - } - return res, nil -} - -@(require_results) -file_size :: proc(fd: Handle) -> (size: i64, err: Error) { - size = -1 - s := _fstat(fd) or_return - size = s.size - return -} - -rename :: proc(old_path, new_path: string) -> Error { - runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD() - old_path_cstr := strings.clone_to_cstring(old_path, context.temp_allocator) - new_path_cstr := strings.clone_to_cstring(new_path, context.temp_allocator) - res := _unix_rename(old_path_cstr, new_path_cstr) - if res == -1 { - return get_last_error() - } - return nil -} - -remove :: proc(path: string) -> Error { - runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD() - path_cstr := strings.clone_to_cstring(path, context.temp_allocator) - res := _unix_unlink(path_cstr) - if res == -1 { - return get_last_error() - } - return nil -} - -make_directory :: proc(path: string, mode: mode_t = 0o775) -> Error { - runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD() - path_cstr := strings.clone_to_cstring(path, context.temp_allocator) - res := _unix_mkdir(path_cstr, mode) - if res == -1 { - return get_last_error() - } - return nil -} - -remove_directory :: proc(path: string) -> Error { - runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD() - path_cstr := strings.clone_to_cstring(path, context.temp_allocator) - res := _unix_rmdir(path_cstr) - if res == -1 { - return get_last_error() - } - return nil -} - -@(require_results) -is_file_handle :: proc(fd: Handle) -> bool { - s, err := _fstat(fd) - if err != nil { - return false - } - return S_ISREG(s.mode) -} - -@(require_results) -is_file_path :: proc(path: string, follow_links: bool = true) -> bool { - s: OS_Stat - err: Error - if follow_links { - s, err = _stat(path) - } else { - s, err = _lstat(path) - } - if err != nil { - return false - } - return S_ISREG(s.mode) -} - -@(require_results) -is_dir_handle :: proc(fd: Handle) -> bool { - s, err := _fstat(fd) - if err != nil { - return false - } - return S_ISDIR(s.mode) -} - -@(require_results) -is_dir_path :: proc(path: string, follow_links: bool = true) -> bool { - s: OS_Stat - err: Error - if follow_links { - s, err = _stat(path) - } else { - s, err = _lstat(path) - } - if err != nil { - return false - } - return S_ISDIR(s.mode) -} - -is_file :: proc {is_file_path, is_file_handle} -is_dir :: proc {is_dir_path, is_dir_handle} - -@(require_results) -exists :: proc(path: string) -> bool { - runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD() - cpath := strings.clone_to_cstring(path, context.temp_allocator) - res := _unix_access(cpath, O_RDONLY) - return res == 0 -} - -@(require_results) -fcntl :: proc(fd: int, cmd: int, arg: int) -> (int, Error) { - result := _unix_fcntl(Handle(fd), c.int(cmd), uintptr(arg)) - if result < 0 { - return 0, get_last_error() - } - return int(result), nil -} - -// NOTE(bill): Uses startup to initialize it - -stdin: Handle = 0 -stdout: Handle = 1 -stderr: Handle = 2 - -@(require_results) -last_write_time :: proc(fd: Handle) -> (time: File_Time, err: Error) { - s := _fstat(fd) or_return - modified := s.modified.seconds * 1_000_000_000 + s.modified.nanoseconds - return File_Time(modified), nil -} - -@(require_results) -last_write_time_by_name :: proc(name: string) -> (time: File_Time, err: Error) { - s := _stat(name) or_return - modified := s.modified.seconds * 1_000_000_000 + s.modified.nanoseconds - return File_Time(modified), nil -} - -@(private, require_results, no_sanitize_memory) -_stat :: proc(path: string) -> (OS_Stat, Error) { - runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD() - cstr := strings.clone_to_cstring(path, context.temp_allocator) - s: OS_Stat = --- - result := _unix_lstat(cstr, &s) - if result == -1 { - return s, get_last_error() - } - return s, nil -} - -@(private, require_results, no_sanitize_memory) -_lstat :: proc(path: string) -> (OS_Stat, Error) { - runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD() - cstr := strings.clone_to_cstring(path, context.temp_allocator) - - // deliberately uninitialized - s: OS_Stat = --- - res := _unix_lstat(cstr, &s) - if res == -1 { - return s, get_last_error() - } - return s, nil -} - -@(private, require_results, no_sanitize_memory) -_fstat :: proc(fd: Handle) -> (OS_Stat, Error) { - s: OS_Stat = --- - result := _unix_fstat(fd, &s) - if result == -1 { - return s, get_last_error() - } - return s, nil -} - -@(private, require_results) -_fdopendir :: proc(fd: Handle) -> (Dir, Error) { - dirp := _unix_fdopendir(fd) - if dirp == cast(Dir)nil { - return nil, get_last_error() - } - return dirp, nil -} - -@(private) -_closedir :: proc(dirp: Dir) -> Error { - rc := _unix_closedir(dirp) - if rc != 0 { - return get_last_error() - } - return nil -} - -@(private) -_rewinddir :: proc(dirp: Dir) { - _unix_rewinddir(dirp) -} - -@(private, require_results) -_readdir :: proc(dirp: Dir) -> (entry: Dirent, err: Error, end_of_stream: bool) { - result: ^Dirent - rc := _unix_readdir_r(dirp, &entry, &result) - - if rc != 0 { - err = get_last_error() - return - } - err = nil - - if result == nil { - end_of_stream = true - return - } - - return -} - -@(private, require_results) -_readlink :: proc(path: string) -> (string, Error) { - runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD(ignore = context.temp_allocator == context.allocator) - - path_cstr := strings.clone_to_cstring(path, context.temp_allocator) - - bufsz : uint = MAX_PATH - buf := make([]byte, MAX_PATH) - for { - rc := _unix_readlink(path_cstr, &(buf[0]), bufsz) - if rc == -1 { - delete(buf) - return "", get_last_error() - } else if rc == int(bufsz) { - bufsz += MAX_PATH - delete(buf) - buf = make([]byte, bufsz) - } else { - return strings.string_from_ptr(&buf[0], rc), nil - } - } - - return "", Error{} -} - -@(private, require_results) -_dup :: proc(fd: Handle) -> (Handle, Error) { - dup := _unix_dup(fd) - if dup == -1 { - return INVALID_HANDLE, get_last_error() - } - return dup, nil -} - -@(require_results) -absolute_path_from_handle :: proc(fd: Handle) -> (path: string, err: Error) { - buf: [MAX_PATH]byte - _ = fcntl(int(fd), F_GETPATH, int(uintptr(&buf[0]))) or_return - return strings.clone_from_cstring(cstring(&buf[0])) -} - -@(require_results) -absolute_path_from_relative :: proc(rel: string, allocator := context.allocator) -> (path: string, err: Error) { - rel := rel - if rel == "" { - rel = "." - } - runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD(ignore = context.temp_allocator == context.allocator) - - rel_cstr := strings.clone_to_cstring(rel, context.temp_allocator) - - path_ptr := _unix_realpath(rel_cstr, nil) - if path_ptr == nil { - return "", get_last_error() - } - defer _unix_free(rawptr(path_ptr)) - - return strings.clone(string(path_ptr), allocator) -} - -access :: proc(path: string, mask: int) -> (bool, Error) { - runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD() - - cstr := strings.clone_to_cstring(path, context.temp_allocator) - result := _unix_access(cstr, c.int(mask)) - if result == -1 { - return false, get_last_error() - } - return true, nil -} - -@(require_results) -lookup_env_alloc :: proc(key: string, allocator := context.allocator) -> (value: string, found: bool) { - runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD(ignore = context.temp_allocator == allocator) - path_str := strings.clone_to_cstring(key, context.temp_allocator) - // NOTE(tetra): Lifetime of 'cstr' is unclear, but _unix_free(cstr) segfaults. - cstr := _unix_getenv(path_str) - if cstr == nil { - return "", false - } - return strings.clone(string(cstr), allocator), true -} - -@(require_results) -lookup_env_buffer :: proc(buf: []u8, key: string) -> (value: string, err: Error) { - if len(key) + 1 > len(buf) { - return "", .Buffer_Full - } else { - copy(buf, key) - buf[len(key)] = 0 - } - - if value = string(_unix_getenv(cstring(raw_data(buf)))); value == "" { - return "", .Env_Var_Not_Found - } else { - if len(value) > len(buf) { - return "", .Buffer_Full - } else { - copy(buf, value) - return string(buf[:len(value)]), nil - } - } -} -lookup_env :: proc{lookup_env_alloc, lookup_env_buffer} - -@(require_results) -get_env_alloc :: proc(key: string, allocator := context.allocator) -> (value: string) { - value, _ = lookup_env(key, allocator) - return -} - -@(require_results) -get_env_buf :: proc(buf: []u8, key: string) -> (value: string) { - value, _ = lookup_env(buf, key) - return -} -get_env :: proc{get_env_alloc, get_env_buf} - -@(require_results) -get_current_directory :: proc(allocator := context.allocator) -> string { - context.allocator = allocator - // NOTE(tetra): I would use PATH_MAX here, but I was not able to find - // an authoritative value for it across all systems. - // The largest value I could find was 4096, so might as well use the page size. - page_size := get_page_size() - buf := make([dynamic]u8, page_size) - #no_bounds_check for { - cwd := _unix_getcwd(cstring(&buf[0]), c.size_t(len(buf))) - if cwd != nil { - return string(cwd) - } - if get_last_error() != ERANGE { - delete(buf) - return "" - } - resize(&buf, len(buf)+page_size) - } - unreachable() -} - -set_current_directory :: proc(path: string) -> (err: Error) { - runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD() - cstr := strings.clone_to_cstring(path, context.temp_allocator) - res := _unix_chdir(cstr) - if res == -1 { - return get_last_error() - } - return nil -} - -exit :: proc "contextless" (code: int) -> ! { - runtime._cleanup_runtime_contextless() - _unix_exit(c.int(code)) -} - -@(require_results) -current_thread_id :: proc "contextless" () -> int { - return int(_lwp_self()) -} - -@(require_results) -dlopen :: proc(filename: string, flags: int) -> rawptr { - runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD() - cstr := strings.clone_to_cstring(filename, context.temp_allocator) - handle := _unix_dlopen(cstr, c.int(flags)) - return handle -} - -@(require_results) -dlsym :: proc(handle: rawptr, symbol: string) -> rawptr { - assert(handle != nil) - runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD() - cstr := strings.clone_to_cstring(symbol, context.temp_allocator) - proc_handle := _unix_dlsym(handle, cstr) - return proc_handle -} - -dlclose :: proc(handle: rawptr) -> bool { - assert(handle != nil) - return _unix_dlclose(handle) == 0 -} - -@(require_results) -dlerror :: proc() -> string { - return string(_unix_dlerror()) -} - -@(require_results) -get_page_size :: proc() -> int { - // NOTE(tetra): The page size never changes, so why do anything complicated - // if we don't have to. - @static page_size := -1 - if page_size != -1 { - return page_size - } - - page_size = int(_unix_getpagesize()) - return page_size -} - -@(private, require_results) -_processor_core_count :: proc() -> int { - count : int = 0 - count_size := size_of(count) - if _sysctlbyname("hw.ncpu", &count, &count_size, nil, 0) == 0 { - if count > 0 { - return count - } - } - - return 1 -} - -@(private, require_results) -_alloc_command_line_arguments :: proc "contextless" () -> []string { - context = runtime.default_context() - res := make([]string, len(runtime.args__)) - for _, i in res { - res[i] = string(runtime.args__[i]) - } - return res -} - -@(private, fini) -_delete_command_line_arguments :: proc "contextless" () { - context = runtime.default_context() - delete(args) -} diff --git a/core/os/old/os_openbsd.odin b/core/os/old/os_openbsd.odin deleted file mode 100644 index 95d431134..000000000 --- a/core/os/old/os_openbsd.odin +++ /dev/null @@ -1,932 +0,0 @@ -package os_old - -foreign import libc "system:c" - -import "core:strings" -import "core:c" -import "base:runtime" - -Handle :: distinct i32 -Pid :: distinct i32 -File_Time :: distinct u64 - -INVALID_HANDLE :: ~Handle(0) - -_Platform_Error :: enum i32 { - NONE = 0, - EPERM = 1, - ENOENT = 2, - ESRCH = 3, - EINTR = 4, - EIO = 5, - ENXIO = 6, - E2BIG = 7, - ENOEXEC = 8, - EBADF = 9, - ECHILD = 10, - EDEADLK = 11, - ENOMEM = 12, - EACCES = 13, - EFAULT = 14, - ENOTBLK = 15, - EBUSY = 16, - EEXIST = 17, - EXDEV = 18, - ENODEV = 19, - ENOTDIR = 20, - EISDIR = 21, - EINVAL = 22, - ENFILE = 23, - EMFILE = 24, - ENOTTY = 25, - ETXTBSY = 26, - EFBIG = 27, - ENOSPC = 28, - ESPIPE = 29, - EROFS = 30, - EMLINK = 31, - EPIPE = 32, - EDOM = 33, - ERANGE = 34, - EAGAIN = 35, - EWOULDBLOCK = EAGAIN, - EINPROGRESS = 36, - EALREADY = 37, - ENOTSOCK = 38, - EDESTADDRREQ = 39, - EMSGSIZE = 40, - EPROTOTYPE = 41, - ENOPROTOOPT = 42, - EPROTONOSUPPORT = 43, - ESOCKTNOSUPPORT = 44, - EOPNOTSUPP = 45, - EPFNOSUPPORT = 46, - EAFNOSUPPORT = 47, - EADDRINUSE = 48, - EADDRNOTAVAIL = 49, - ENETDOWN = 50, - ENETUNREACH = 51, - ENETRESET = 52, - ECONNABORTED = 53, - ECONNRESET = 54, - ENOBUFS = 55, - EISCONN = 56, - ENOTCONN = 57, - ESHUTDOWN = 58, - ETOOMANYREFS = 59, - ETIMEDOUT = 60, - ECONNREFUSED = 61, - ELOOP = 62, - ENAMETOOLONG = 63, - EHOSTDOWN = 64, - EHOSTUNREACH = 65, - ENOTEMPTY = 66, - EPROCLIM = 67, - EUSERS = 68, - EDQUOT = 69, - ESTALE = 70, - EREMOTE = 71, - EBADRPC = 72, - ERPCMISMATCH = 73, - EPROGUNAVAIL = 74, - EPROGMISMATCH = 75, - EPROCUNAVAIL = 76, - ENOLCK = 77, - ENOSYS = 78, - EFTYPE = 79, - EAUTH = 80, - ENEEDAUTH = 81, - EIPSEC = 82, - ENOATTR = 83, - EILSEQ = 84, - ENOMEDIUM = 85, - EMEDIUMTYPE = 86, - EOVERFLOW = 87, - ECANCELED = 88, - EIDRM = 89, - ENOMSG = 90, - ENOTSUP = 91, - EBADMSG = 92, - ENOTRECOVERABLE = 93, - EOWNERDEAD = 94, - EPROTO = 95, -} - -EPERM :: Platform_Error.EPERM -ENOENT :: Platform_Error.ENOENT -ESRCH :: Platform_Error.ESRCH -EINTR :: Platform_Error.EINTR -EIO :: Platform_Error.EIO -ENXIO :: Platform_Error.ENXIO -E2BIG :: Platform_Error.E2BIG -ENOEXEC :: Platform_Error.ENOEXEC -EBADF :: Platform_Error.EBADF -ECHILD :: Platform_Error.ECHILD -EDEADLK :: Platform_Error.EDEADLK -ENOMEM :: Platform_Error.ENOMEM -EACCES :: Platform_Error.EACCES -EFAULT :: Platform_Error.EFAULT -ENOTBLK :: Platform_Error.ENOTBLK -EBUSY :: Platform_Error.EBUSY -EEXIST :: Platform_Error.EEXIST -EXDEV :: Platform_Error.EXDEV -ENODEV :: Platform_Error.ENODEV -ENOTDIR :: Platform_Error.ENOTDIR -EISDIR :: Platform_Error.EISDIR -EINVAL :: Platform_Error.EINVAL -ENFILE :: Platform_Error.ENFILE -EMFILE :: Platform_Error.EMFILE -ENOTTY :: Platform_Error.ENOTTY -ETXTBSY :: Platform_Error.ETXTBSY -EFBIG :: Platform_Error.EFBIG -ENOSPC :: Platform_Error.ENOSPC -ESPIPE :: Platform_Error.ESPIPE -EROFS :: Platform_Error.EROFS -EMLINK :: Platform_Error.EMLINK -EPIPE :: Platform_Error.EPIPE -EDOM :: Platform_Error.EDOM -ERANGE :: Platform_Error.ERANGE -EAGAIN :: Platform_Error.EAGAIN -EWOULDBLOCK :: Platform_Error.EWOULDBLOCK -EINPROGRESS :: Platform_Error.EINPROGRESS -EALREADY :: Platform_Error.EALREADY -ENOTSOCK :: Platform_Error.ENOTSOCK -EDESTADDRREQ :: Platform_Error.EDESTADDRREQ -EMSGSIZE :: Platform_Error.EMSGSIZE -EPROTOTYPE :: Platform_Error.EPROTOTYPE -ENOPROTOOPT :: Platform_Error.ENOPROTOOPT -EPROTONOSUPPORT :: Platform_Error.EPROTONOSUPPORT -ESOCKTNOSUPPORT :: Platform_Error.ESOCKTNOSUPPORT -EOPNOTSUPP :: Platform_Error.EOPNOTSUPP -EPFNOSUPPORT :: Platform_Error.EPFNOSUPPORT -EAFNOSUPPORT :: Platform_Error.EAFNOSUPPORT -EADDRINUSE :: Platform_Error.EADDRINUSE -EADDRNOTAVAIL :: Platform_Error.EADDRNOTAVAIL -ENETDOWN :: Platform_Error.ENETDOWN -ENETUNREACH :: Platform_Error.ENETUNREACH -ENETRESET :: Platform_Error.ENETRESET -ECONNABORTED :: Platform_Error.ECONNABORTED -ECONNRESET :: Platform_Error.ECONNRESET -ENOBUFS :: Platform_Error.ENOBUFS -EISCONN :: Platform_Error.EISCONN -ENOTCONN :: Platform_Error.ENOTCONN -ESHUTDOWN :: Platform_Error.ESHUTDOWN -ETOOMANYREFS :: Platform_Error.ETOOMANYREFS -ETIMEDOUT :: Platform_Error.ETIMEDOUT -ECONNREFUSED :: Platform_Error.ECONNREFUSED -ELOOP :: Platform_Error.ELOOP -ENAMETOOLONG :: Platform_Error.ENAMETOOLONG -EHOSTDOWN :: Platform_Error.EHOSTDOWN -EHOSTUNREACH :: Platform_Error.EHOSTUNREACH -ENOTEMPTY :: Platform_Error.ENOTEMPTY -EPROCLIM :: Platform_Error.EPROCLIM -EUSERS :: Platform_Error.EUSERS -EDQUOT :: Platform_Error.EDQUOT -ESTALE :: Platform_Error.ESTALE -EREMOTE :: Platform_Error.EREMOTE -EBADRPC :: Platform_Error.EBADRPC -ERPCMISMATCH :: Platform_Error.ERPCMISMATCH -EPROGUNAVAIL :: Platform_Error.EPROGUNAVAIL -EPROGMISMATCH :: Platform_Error.EPROGMISMATCH -EPROCUNAVAIL :: Platform_Error.EPROCUNAVAIL -ENOLCK :: Platform_Error.ENOLCK -ENOSYS :: Platform_Error.ENOSYS -EFTYPE :: Platform_Error.EFTYPE -EAUTH :: Platform_Error.EAUTH -ENEEDAUTH :: Platform_Error.ENEEDAUTH -EIPSEC :: Platform_Error.EIPSEC -ENOATTR :: Platform_Error.ENOATTR -EILSEQ :: Platform_Error.EILSEQ -ENOMEDIUM :: Platform_Error.ENOMEDIUM -EMEDIUMTYPE :: Platform_Error.EMEDIUMTYPE -EOVERFLOW :: Platform_Error.EOVERFLOW -ECANCELED :: Platform_Error.ECANCELED -EIDRM :: Platform_Error.EIDRM -ENOMSG :: Platform_Error.ENOMSG -ENOTSUP :: Platform_Error.ENOTSUP -EBADMSG :: Platform_Error.EBADMSG -ENOTRECOVERABLE :: Platform_Error.ENOTRECOVERABLE -EOWNERDEAD :: Platform_Error.EOWNERDEAD -EPROTO :: Platform_Error.EPROTO - -O_RDONLY :: 0x00000 -O_WRONLY :: 0x00001 -O_RDWR :: 0x00002 -O_NONBLOCK :: 0x00004 -O_APPEND :: 0x00008 -O_ASYNC :: 0x00040 -O_SYNC :: 0x00080 -O_CREATE :: 0x00200 -O_TRUNC :: 0x00400 -O_EXCL :: 0x00800 -O_NOCTTY :: 0x08000 -O_CLOEXEC :: 0x10000 - -RTLD_LAZY :: 0x001 -RTLD_NOW :: 0x002 -RTLD_LOCAL :: 0x000 -RTLD_GLOBAL :: 0x100 -RTLD_TRACE :: 0x200 -RTLD_NODELETE :: 0x400 - -MAX_PATH :: 1024 - -// "Argv" arguments converted to Odin strings -args := _alloc_command_line_arguments() - -pid_t :: i32 -time_t :: i64 -mode_t :: u32 -dev_t :: i32 -ino_t :: u64 -nlink_t :: u32 -uid_t :: u32 -gid_t :: u32 -off_t :: i64 -blkcnt_t :: u64 -blksize_t :: i32 - -Unix_File_Time :: struct { - seconds: time_t, - nanoseconds: c.long, -} - -OS_Stat :: struct { - mode: mode_t, // inode protection mode - device_id: dev_t, // inode's device - serial: ino_t, // inode's number - nlink: nlink_t, // number of hard links - uid: uid_t, // user ID of the file's owner - gid: gid_t, // group ID of the file's group - rdev: dev_t, // device type - - last_access: Unix_File_Time, // time of last access - modified: Unix_File_Time, // time of last data modification - status_change: Unix_File_Time, // time of last file status change - - size: off_t, // file size, in bytes - blocks: blkcnt_t, // blocks allocated for file - block_size: blksize_t, // optimal blocksize for I/O - - flags: u32, // user defined flags for file - gen: u32, // file generation number - birthtime: Unix_File_Time, // time of file creation -} - -MAXNAMLEN :: 255 - -// NOTE(laleksic, 2021-01-21): Comment and rename these to match OS_Stat above -Dirent :: struct { - ino: ino_t, // file number of entry - off: off_t, // offset after this entry - reclen: u16, // length of this record - type: u8, // file type - namlen: u8, // length of string in name - _padding: [4]u8, - name: [MAXNAMLEN + 1]byte, // name -} - -Dir :: distinct rawptr // DIR* - -// File type -S_IFMT :: 0o170000 // Type of file mask -S_IFIFO :: 0o010000 // Named pipe (fifo) -S_IFCHR :: 0o020000 // Character special -S_IFDIR :: 0o040000 // Directory -S_IFBLK :: 0o060000 // Block special -S_IFREG :: 0o100000 // Regular -S_IFLNK :: 0o120000 // Symbolic link -S_IFSOCK :: 0o140000 // Socket -S_ISVTX :: 0o001000 // Save swapped text even after use - -// File mode - // Read, write, execute/search by owner -S_IRWXU :: 0o0700 // RWX mask for owner -S_IRUSR :: 0o0400 // R for owner -S_IWUSR :: 0o0200 // W for owner -S_IXUSR :: 0o0100 // X for owner - - // Read, write, execute/search by group -S_IRWXG :: 0o0070 // RWX mask for group -S_IRGRP :: 0o0040 // R for group -S_IWGRP :: 0o0020 // W for group -S_IXGRP :: 0o0010 // X for group - - // Read, write, execute/search by others -S_IRWXO :: 0o0007 // RWX mask for other -S_IROTH :: 0o0004 // R for other -S_IWOTH :: 0o0002 // W for other -S_IXOTH :: 0o0001 // X for other - -S_ISUID :: 0o4000 // Set user id on execution -S_ISGID :: 0o2000 // Set group id on execution -S_ISTXT :: 0o1000 // Sticky bit - -@(require_results) S_ISLNK :: #force_inline proc(m: u32) -> bool { return (m & S_IFMT) == S_IFLNK } -@(require_results) S_ISREG :: #force_inline proc(m: u32) -> bool { return (m & S_IFMT) == S_IFREG } -@(require_results) S_ISDIR :: #force_inline proc(m: u32) -> bool { return (m & S_IFMT) == S_IFDIR } -@(require_results) S_ISCHR :: #force_inline proc(m: u32) -> bool { return (m & S_IFMT) == S_IFCHR } -@(require_results) S_ISBLK :: #force_inline proc(m: u32) -> bool { return (m & S_IFMT) == S_IFBLK } -@(require_results) S_ISFIFO :: #force_inline proc(m: u32) -> bool { return (m & S_IFMT) == S_IFIFO } -@(require_results) S_ISSOCK :: #force_inline proc(m: u32) -> bool { return (m & S_IFMT) == S_IFSOCK } - -F_OK :: 0x00 // Test for file existance -X_OK :: 0x01 // Test for execute permission -W_OK :: 0x02 // Test for write permission -R_OK :: 0x04 // Test for read permission - -AT_FDCWD :: -100 -AT_EACCESS :: 0x01 -AT_SYMLINK_NOFOLLOW :: 0x02 -AT_SYMLINK_FOLLOW :: 0x04 -AT_REMOVEDIR :: 0x08 - -@(default_calling_convention="c") -foreign libc { - @(link_name="__errno") __error :: proc() -> ^c.int --- - - @(link_name="fork") _unix_fork :: proc() -> pid_t --- - @(link_name="getthrid") _unix_getthrid :: proc() -> int --- - - @(link_name="open") _unix_open :: proc(path: cstring, flags: c.int, #c_vararg mode: ..u32) -> Handle --- - @(link_name="close") _unix_close :: proc(fd: Handle) -> c.int --- - @(link_name="read") _unix_read :: proc(fd: Handle, buf: rawptr, size: c.size_t) -> c.ssize_t --- - @(link_name="pread") _unix_pread :: proc(fd: Handle, buf: rawptr, size: c.size_t, offset: i64) -> c.ssize_t --- - @(link_name="write") _unix_write :: proc(fd: Handle, buf: rawptr, size: c.size_t) -> c.ssize_t --- - @(link_name="pwrite") _unix_pwrite :: proc(fd: Handle, buf: rawptr, size: c.size_t, offset: i64) -> c.ssize_t --- - @(link_name="lseek") _unix_seek :: proc(fd: Handle, offset: off_t, whence: c.int) -> off_t --- - @(link_name="stat") _unix_stat :: proc(path: cstring, sb: ^OS_Stat) -> c.int --- - @(link_name="fstat") _unix_fstat :: proc(fd: Handle, sb: ^OS_Stat) -> c.int --- - @(link_name="lstat") _unix_lstat :: proc(path: cstring, sb: ^OS_Stat) -> c.int --- - @(link_name="readlink") _unix_readlink :: proc(path: cstring, buf: ^byte, bufsiz: c.size_t) -> c.ssize_t --- - @(link_name="access") _unix_access :: proc(path: cstring, mask: c.int) -> c.int --- - @(link_name="getcwd") _unix_getcwd :: proc(buf: cstring, len: c.size_t) -> cstring --- - @(link_name="chdir") _unix_chdir :: proc(path: cstring) -> c.int --- - @(link_name="rename") _unix_rename :: proc(old, new: cstring) -> c.int --- - @(link_name="unlink") _unix_unlink :: proc(path: cstring) -> c.int --- - @(link_name="rmdir") _unix_rmdir :: proc(path: cstring) -> c.int --- - @(link_name="mkdir") _unix_mkdir :: proc(path: cstring, mode: mode_t) -> c.int --- - @(link_name="fsync") _unix_fsync :: proc(fd: Handle) -> c.int --- - @(link_name="dup") _unix_dup :: proc(fd: Handle) -> Handle --- - - @(link_name="getpagesize") _unix_getpagesize :: proc() -> c.int --- - @(link_name="sysconf") _sysconf :: proc(name: c.int) -> c.long --- - @(link_name="fdopendir") _unix_fdopendir :: proc(fd: Handle) -> Dir --- - @(link_name="closedir") _unix_closedir :: proc(dirp: Dir) -> c.int --- - @(link_name="rewinddir") _unix_rewinddir :: proc(dirp: Dir) --- - @(link_name="readdir_r") _unix_readdir_r :: proc(dirp: Dir, entry: ^Dirent, result: ^^Dirent) -> c.int --- - - @(link_name="malloc") _unix_malloc :: proc(size: c.size_t) -> rawptr --- - @(link_name="calloc") _unix_calloc :: proc(num, size: c.size_t) -> rawptr --- - @(link_name="free") _unix_free :: proc(ptr: rawptr) --- - @(link_name="realloc") _unix_realloc :: proc(ptr: rawptr, size: c.size_t) -> rawptr --- - - @(link_name="getenv") _unix_getenv :: proc(cstring) -> cstring --- - @(link_name="realpath") _unix_realpath :: proc(path: cstring, resolved_path: [^]byte = nil) -> cstring --- - - @(link_name="exit") _unix_exit :: proc(status: c.int) -> ! --- - - @(link_name="dlopen") _unix_dlopen :: proc(filename: cstring, flags: c.int) -> rawptr --- - @(link_name="dlsym") _unix_dlsym :: proc(handle: rawptr, symbol: cstring) -> rawptr --- - @(link_name="dlclose") _unix_dlclose :: proc(handle: rawptr) -> c.int --- - @(link_name="dlerror") _unix_dlerror :: proc() -> cstring --- -} - -@(require_results) -is_path_separator :: proc(r: rune) -> bool { - return r == '/' -} - -@(require_results, no_instrumentation) -get_last_error :: proc "contextless" () -> Error { - return Platform_Error(__error()^) -} - -@(require_results) -fork :: proc() -> (Pid, Error) { - pid := _unix_fork() - if pid == -1 { - return Pid(-1), get_last_error() - } - return Pid(pid), nil -} - -@(require_results) -open :: proc(path: string, flags: int = O_RDONLY, mode: int = 0) -> (Handle, Error) { - runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD() - cstr := strings.clone_to_cstring(path, context.temp_allocator) - handle := _unix_open(cstr, c.int(flags), c.uint(mode)) - if handle == -1 { - return INVALID_HANDLE, get_last_error() - } - return handle, nil -} - -close :: proc(fd: Handle) -> Error { - result := _unix_close(fd) - if result == -1 { - return get_last_error() - } - return nil -} - -flush :: proc(fd: Handle) -> Error { - result := _unix_fsync(fd) - if result == -1 { - return get_last_error() - } - return nil -} - -// If you read or write more than `SSIZE_MAX` bytes, OpenBSD returns `EINVAL`. -// In practice a read/write call would probably never read/write these big buffers all at once, -// which is why the number of bytes is returned and why there are procs that will call this in a -// loop for you. -// We set a max of 1GB to keep alignment and to be safe. -@(private) -MAX_RW :: 1 << 30 - -read :: proc(fd: Handle, data: []byte) -> (int, Error) { - to_read := min(c.size_t(len(data)), MAX_RW) - bytes_read := _unix_read(fd, &data[0], to_read) - if bytes_read == -1 { - return -1, get_last_error() - } - return int(bytes_read), nil -} - -write :: proc(fd: Handle, data: []byte) -> (int, Error) { - if len(data) == 0 { - return 0, nil - } - - to_write := min(c.size_t(len(data)), MAX_RW) - bytes_written := _unix_write(fd, &data[0], to_write) - if bytes_written == -1 { - return -1, get_last_error() - } - return int(bytes_written), nil -} - -read_at :: proc(fd: Handle, data: []byte, offset: i64) -> (n: int, err: Error) { - if len(data) == 0 { - return 0, nil - } - - to_read := min(uint(len(data)), MAX_RW) - - bytes_read := _unix_pread(fd, raw_data(data), to_read, offset) - if bytes_read < 0 { - return -1, get_last_error() - } - return bytes_read, nil -} - -write_at :: proc(fd: Handle, data: []byte, offset: i64) -> (n: int, err: Error) { - if len(data) == 0 { - return 0, nil - } - - to_write := min(uint(len(data)), MAX_RW) - - bytes_written := _unix_pwrite(fd, raw_data(data), to_write, offset) - if bytes_written < 0 { - return -1, get_last_error() - } - return bytes_written, nil -} - -seek :: proc(fd: Handle, offset: i64, whence: int) -> (i64, Error) { - switch whence { - case SEEK_SET, SEEK_CUR, SEEK_END: - break - case: - return 0, .Invalid_Whence - } - res := _unix_seek(fd, offset, c.int(whence)) - if res == -1 { - errno := get_last_error() - switch errno { - case .EINVAL: - return 0, .Invalid_Offset - } - return 0, errno - } - return res, nil -} - -@(require_results) -file_size :: proc(fd: Handle) -> (size: i64, err: Error) { - size = -1 - s := _fstat(fd) or_return - size = s.size - return -} - -rename :: proc(old_path, new_path: string) -> Error { - runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD() - old_path_cstr := strings.clone_to_cstring(old_path, context.temp_allocator) - new_path_cstr := strings.clone_to_cstring(new_path, context.temp_allocator) - res := _unix_rename(old_path_cstr, new_path_cstr) - if res == -1 { - return get_last_error() - } - return nil -} - -remove :: proc(path: string) -> Error { - runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD() - path_cstr := strings.clone_to_cstring(path, context.temp_allocator) - res := _unix_unlink(path_cstr) - if res == -1 { - return get_last_error() - } - return nil -} - -make_directory :: proc(path: string, mode: mode_t = 0o775) -> Error { - runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD() - path_cstr := strings.clone_to_cstring(path, context.temp_allocator) - res := _unix_mkdir(path_cstr, mode) - if res == -1 { - return get_last_error() - } - return nil -} - -remove_directory :: proc(path: string) -> Error { - runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD() - path_cstr := strings.clone_to_cstring(path, context.temp_allocator) - res := _unix_rmdir(path_cstr) - if res == -1 { - return get_last_error() - } - return nil -} - -@(require_results) -is_file_handle :: proc(fd: Handle) -> bool { - s, err := _fstat(fd) - if err != nil { - return false - } - return S_ISREG(s.mode) -} - -@(require_results) -is_file_path :: proc(path: string, follow_links: bool = true) -> bool { - s: OS_Stat - err: Error - if follow_links { - s, err = _stat(path) - } else { - s, err = _lstat(path) - } - if err != nil { - return false - } - return S_ISREG(s.mode) -} - -@(require_results) -is_dir_handle :: proc(fd: Handle) -> bool { - s, err := _fstat(fd) - if err != nil { - return false - } - return S_ISDIR(s.mode) -} - -@(require_results) -is_dir_path :: proc(path: string, follow_links: bool = true) -> bool { - s: OS_Stat - err: Error - if follow_links { - s, err = _stat(path) - } else { - s, err = _lstat(path) - } - if err != nil { - return false - } - return S_ISDIR(s.mode) -} - -is_file :: proc {is_file_path, is_file_handle} -is_dir :: proc {is_dir_path, is_dir_handle} - -// NOTE(bill): Uses startup to initialize it - -stdin: Handle = 0 -stdout: Handle = 1 -stderr: Handle = 2 - -/* TODO(zangent): Implement these! -last_write_time :: proc(fd: Handle) -> File_Time {} -last_write_time_by_name :: proc(name: string) -> File_Time {} -*/ -@(require_results) -last_write_time :: proc(fd: Handle) -> (time: File_Time, err: Error) { - s := _fstat(fd) or_return - modified := s.modified.seconds * 1_000_000_000 + s.modified.nanoseconds - return File_Time(modified), nil -} - -@(require_results) -last_write_time_by_name :: proc(name: string) -> (time: File_Time, err: Error) { - s := _stat(name) or_return - modified := s.modified.seconds * 1_000_000_000 + s.modified.nanoseconds - return File_Time(modified), nil -} - -@(private, require_results, no_sanitize_memory) -_stat :: proc(path: string) -> (OS_Stat, Error) { - runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD() - cstr := strings.clone_to_cstring(path, context.temp_allocator) - - // deliberately uninitialized - s: OS_Stat = --- - res := _unix_stat(cstr, &s) - if res == -1 { - return s, get_last_error() - } - return s, nil -} - -@(private, require_results, no_sanitize_memory) -_lstat :: proc(path: string) -> (OS_Stat, Error) { - runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD() - cstr := strings.clone_to_cstring(path, context.temp_allocator) - - // deliberately uninitialized - s: OS_Stat = --- - res := _unix_lstat(cstr, &s) - if res == -1 { - return s, get_last_error() - } - return s, nil -} - -@(private, require_results, no_sanitize_memory) -_fstat :: proc(fd: Handle) -> (OS_Stat, Error) { - // deliberately uninitialized - s: OS_Stat = --- - res := _unix_fstat(fd, &s) - if res == -1 { - return s, get_last_error() - } - return s, nil -} - -@(private, require_results) -_fdopendir :: proc(fd: Handle) -> (Dir, Error) { - dirp := _unix_fdopendir(fd) - if dirp == cast(Dir)nil { - return nil, get_last_error() - } - return dirp, nil -} - -@(private) -_closedir :: proc(dirp: Dir) -> Error { - rc := _unix_closedir(dirp) - if rc != 0 { - return get_last_error() - } - return nil -} - -@(private) -_rewinddir :: proc(dirp: Dir) { - _unix_rewinddir(dirp) -} - -@(private, require_results) -_readdir :: proc(dirp: Dir) -> (entry: Dirent, err: Error, end_of_stream: bool) { - result: ^Dirent - rc := _unix_readdir_r(dirp, &entry, &result) - - if rc != 0 { - err = get_last_error() - return - } - err = nil - - if result == nil { - end_of_stream = true - return - } - - return -} - -@(private, require_results) -_readlink :: proc(path: string) -> (string, Error) { - runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD(ignore = context.temp_allocator == context.allocator) - path_cstr := strings.clone_to_cstring(path, context.temp_allocator) - - bufsz : uint = MAX_PATH - buf := make([]byte, MAX_PATH) - for { - rc := _unix_readlink(path_cstr, &(buf[0]), bufsz) - if rc == -1 { - delete(buf) - return "", get_last_error() - } else if rc == int(bufsz) { - bufsz += MAX_PATH - delete(buf) - buf = make([]byte, bufsz) - } else { - return strings.string_from_ptr(&buf[0], rc), nil - } - } -} - -@(private, require_results) -_dup :: proc(fd: Handle) -> (Handle, Error) { - dup := _unix_dup(fd) - if dup == -1 { - return INVALID_HANDLE, get_last_error() - } - return dup, nil -} - -// XXX OpenBSD -@(require_results) -absolute_path_from_handle :: proc(fd: Handle) -> (string, Error) { - return "", Error(ENOSYS) -} - -@(require_results) -absolute_path_from_relative :: proc(rel: string, allocator := context.allocator) -> (path: string, err: Error) { - rel := rel - if rel == "" { - rel = "." - } - - runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD(ignore = context.temp_allocator == context.allocator) - rel_cstr := strings.clone_to_cstring(rel, context.temp_allocator) - - path_ptr := _unix_realpath(rel_cstr, nil) - if path_ptr == nil { - return "", get_last_error() - } - defer _unix_free(rawptr(path_ptr)) - - return strings.clone(string(path_ptr), allocator) -} - -access :: proc(path: string, mask: int) -> (bool, Error) { - runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD() - cstr := strings.clone_to_cstring(path, context.temp_allocator) - res := _unix_access(cstr, c.int(mask)) - if res == -1 { - return false, get_last_error() - } - return true, nil -} - -@(require_results) -lookup_env_alloc :: proc(key: string, allocator := context.allocator) -> (value: string, found: bool) { - runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD(ignore = context.temp_allocator == allocator) - path_str := strings.clone_to_cstring(key, context.temp_allocator) - // NOTE(tetra): Lifetime of 'cstr' is unclear, but _unix_free(cstr) segfaults. - cstr := _unix_getenv(path_str) - if cstr == nil { - return "", false - } - return strings.clone(string(cstr), allocator), true -} - -@(require_results) -lookup_env_buffer :: proc(buf: []u8, key: string) -> (value: string, err: Error) { - if len(key) + 1 > len(buf) { - return "", .Buffer_Full - } else { - copy(buf, key) - buf[len(key)] = 0 - } - - if value = string(_unix_getenv(cstring(raw_data(buf)))); value == "" { - return "", .Env_Var_Not_Found - } else { - if len(value) > len(buf) { - return "", .Buffer_Full - } else { - copy(buf, value) - return string(buf[:len(value)]), nil - } - } -} -lookup_env :: proc{lookup_env_alloc, lookup_env_buffer} - -@(require_results) -get_env_alloc :: proc(key: string, allocator := context.allocator) -> (value: string) { - value, _ = lookup_env(key, allocator) - return -} - -@(require_results) -get_env_buf :: proc(buf: []u8, key: string) -> (value: string) { - value, _ = lookup_env(buf, key) - return -} -get_env :: proc{get_env_alloc, get_env_buf} - -@(require_results) -get_current_directory :: proc(allocator := context.allocator) -> string { - context.allocator = allocator - buf := make([dynamic]u8, MAX_PATH) - for { - cwd := _unix_getcwd(cstring(raw_data(buf)), c.size_t(len(buf))) - if cwd != nil { - return string(cwd) - } - if get_last_error() != ERANGE { - delete(buf) - return "" - } - resize(&buf, len(buf) + MAX_PATH) - } - unreachable() -} - -set_current_directory :: proc(path: string) -> (err: Error) { - runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD() - cstr := strings.clone_to_cstring(path, context.temp_allocator) - res := _unix_chdir(cstr) - if res == -1 { - return get_last_error() - } - return nil -} - -exit :: proc "contextless" (code: int) -> ! { - runtime._cleanup_runtime_contextless() - _unix_exit(c.int(code)) -} - -@(require_results) -current_thread_id :: proc "contextless" () -> int { - return _unix_getthrid() -} - -@(require_results) -dlopen :: proc(filename: string, flags: int) -> rawptr { - runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD() - cstr := strings.clone_to_cstring(filename, context.temp_allocator) - handle := _unix_dlopen(cstr, c.int(flags)) - return handle -} -@(require_results) -dlsym :: proc(handle: rawptr, symbol: string) -> rawptr { - assert(handle != nil) - runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD() - cstr := strings.clone_to_cstring(symbol, context.temp_allocator) - proc_handle := _unix_dlsym(handle, cstr) - return proc_handle -} -dlclose :: proc(handle: rawptr) -> bool { - assert(handle != nil) - return _unix_dlclose(handle) == 0 -} -@(require_results) -dlerror :: proc() -> string { - return string(_unix_dlerror()) -} - -@(require_results) -get_page_size :: proc() -> int { - // NOTE(tetra): The page size never changes, so why do anything complicated - // if we don't have to. - @static page_size := -1 - if page_size != -1 { - return page_size - } - - page_size = int(_unix_getpagesize()) - return page_size -} - -_SC_NPROCESSORS_ONLN :: 503 - -@(private, require_results) -_processor_core_count :: proc() -> int { - return int(_sysconf(_SC_NPROCESSORS_ONLN)) -} - -@(private, require_results) -_alloc_command_line_arguments :: proc "contextless" () -> []string { - context = runtime.default_context() - res := make([]string, len(runtime.args__)) - for _, i in res { - res[i] = string(runtime.args__[i]) - } - return res -} - -@(private, fini) -_delete_command_line_arguments :: proc "contextless" () { - context = runtime.default_context() - delete(args) -} diff --git a/core/os/old/os_wasi.odin b/core/os/old/os_wasi.odin deleted file mode 100644 index 287034957..000000000 --- a/core/os/old/os_wasi.odin +++ /dev/null @@ -1,273 +0,0 @@ -package os_old - -import "core:sys/wasm/wasi" -import "base:runtime" - -Handle :: distinct i32 -_Platform_Error :: wasi.errno_t - -INVALID_HANDLE :: -1 - -O_RDONLY :: 0x00000 -O_WRONLY :: 0x00001 -O_RDWR :: 0x00002 -O_CREATE :: 0x00040 -O_EXCL :: 0x00080 -O_NOCTTY :: 0x00100 -O_TRUNC :: 0x00200 -O_NONBLOCK :: 0x00800 -O_APPEND :: 0x00400 -O_SYNC :: 0x01000 -O_ASYNC :: 0x02000 -O_CLOEXEC :: 0x80000 - -stdin: Handle = 0 -stdout: Handle = 1 -stderr: Handle = 2 - -args := _alloc_command_line_arguments() - -@(private, require_results) -_alloc_command_line_arguments :: proc "contextless" () -> []string { - context = runtime.default_context() - cmd_args := make([]string, len(runtime.args__)) - for &arg, i in cmd_args { - arg = string(runtime.args__[i]) - } - return cmd_args -} - -@(private, fini) -_delete_command_line_arguments :: proc "contextless" () { - context = runtime.default_context() - delete(args) -} - -// WASI works with "preopened" directories, the environment retrieves directories -// (for example with `wasmtime --dir=. module.wasm`) and those given directories -// are the only ones accessible by the application. -// -// So in order to facilitate the `os` API (absolute paths etc.) we keep a list -// of the given directories and match them when needed (notably `os.open`). - -@(private) -Preopen :: struct { - fd: wasi.fd_t, - prefix: string, -} -@(private) -preopens: []Preopen - -@(init, private) -init_preopens :: proc "contextless" () { - strip_prefixes :: proc "contextless"(path: string) -> string { - path := path - loop: for len(path) > 0 { - switch { - case path[0] == '/': - path = path[1:] - case len(path) > 2 && path[0] == '.' && path[1] == '/': - path = path[2:] - case len(path) == 1 && path[0] == '.': - path = path[1:] - case: - break loop - } - } - return path - } - - context = runtime.default_context() - - dyn_preopens: [dynamic]Preopen - loop: for fd := wasi.fd_t(3); ; fd += 1 { - desc, err := wasi.fd_prestat_get(fd) - #partial switch err { - case .BADF: break loop - case: panic("fd_prestat_get returned an unexpected error") - case .SUCCESS: - } - - switch desc.tag { - case .DIR: - buf := make([]byte, desc.dir.pr_name_len) or_else panic("could not allocate memory for filesystem preopens") - if err = wasi.fd_prestat_dir_name(fd, buf); err != .SUCCESS { - panic("could not get filesystem preopen dir name") - } - append(&dyn_preopens, Preopen{fd, strip_prefixes(string(buf))}) - } - } - preopens = dyn_preopens[:] -} - -@(require_results) -wasi_match_preopen :: proc(path: string) -> (wasi.fd_t, string, bool) { - @(require_results) - prefix_matches :: proc(prefix, path: string) -> bool { - // Empty is valid for any relative path. - if len(prefix) == 0 && len(path) > 0 && path[0] != '/' { - return true - } - - if len(path) < len(prefix) { - return false - } - - if path[:len(prefix)] != prefix { - return false - } - - // Only match on full components. - i := len(prefix) - for i > 0 && prefix[i-1] == '/' { - i -= 1 - } - return path[i] == '/' - } - - path := path - for len(path) > 0 && path[0] == '/' { - path = path[1:] - } - - match: Preopen - #reverse for preopen in preopens { - if (match.fd == 0 || len(preopen.prefix) > len(match.prefix)) && prefix_matches(preopen.prefix, path) { - match = preopen - } - } - - if match.fd == 0 { - return 0, "", false - } - - relative := path[len(match.prefix):] - for len(relative) > 0 && relative[0] == '/' { - relative = relative[1:] - } - - if len(relative) == 0 { - relative = "." - } - - return match.fd, relative, true -} - -write :: proc(fd: Handle, data: []byte) -> (int, Errno) { - iovs := wasi.ciovec_t(data) - n, err := wasi.fd_write(wasi.fd_t(fd), {iovs}) - return int(n), Platform_Error(err) -} -read :: proc(fd: Handle, data: []byte) -> (int, Errno) { - iovs := wasi.iovec_t(data) - n, err := wasi.fd_read(wasi.fd_t(fd), {iovs}) - return int(n), Platform_Error(err) -} -write_at :: proc(fd: Handle, data: []byte, offset: i64) -> (int, Errno) { - iovs := wasi.ciovec_t(data) - n, err := wasi.fd_pwrite(wasi.fd_t(fd), {iovs}, wasi.filesize_t(offset)) - return int(n), Platform_Error(err) -} -read_at :: proc(fd: Handle, data: []byte, offset: i64) -> (int, Errno) { - iovs := wasi.iovec_t(data) - n, err := wasi.fd_pread(wasi.fd_t(fd), {iovs}, wasi.filesize_t(offset)) - return int(n), Platform_Error(err) -} -@(require_results) -open :: proc(path: string, mode: int = O_RDONLY, perm: int = 0) -> (Handle, Errno) { - oflags: wasi.oflags_t - if mode & O_CREATE == O_CREATE { - oflags += {.CREATE} - } - if mode & O_EXCL == O_EXCL { - oflags += {.EXCL} - } - if mode & O_TRUNC == O_TRUNC { - oflags += {.TRUNC} - } - - rights: wasi.rights_t = {.FD_SEEK, .FD_FILESTAT_GET} - switch mode & (O_RDONLY|O_WRONLY|O_RDWR) { - case O_RDONLY: rights += {.FD_READ} - case O_WRONLY: rights += {.FD_WRITE} - case O_RDWR: rights += {.FD_READ, .FD_WRITE} - } - - fdflags: wasi.fdflags_t - if mode & O_APPEND == O_APPEND { - fdflags += {.APPEND} - } - if mode & O_NONBLOCK == O_NONBLOCK { - fdflags += {.NONBLOCK} - } - if mode & O_SYNC == O_SYNC { - fdflags += {.SYNC} - } - - dir_fd, relative, ok := wasi_match_preopen(path) - if !ok { - return INVALID_HANDLE, Errno(wasi.errno_t.BADF) - } - - fd, err := wasi.path_open(dir_fd, {.SYMLINK_FOLLOW}, relative, oflags, rights, {}, fdflags) - return Handle(fd), Platform_Error(err) -} -close :: proc(fd: Handle) -> Errno { - err := wasi.fd_close(wasi.fd_t(fd)) - return Platform_Error(err) -} - -flush :: proc(fd: Handle) -> Error { - // do nothing - return nil -} - -seek :: proc(fd: Handle, offset: i64, whence: int) -> (i64, Errno) { - n, err := wasi.fd_seek(wasi.fd_t(fd), wasi.filedelta_t(offset), wasi.whence_t(whence)) - return i64(n), Platform_Error(err) -} -@(require_results) -current_thread_id :: proc "contextless" () -> int { - return 0 -} -@(private, require_results) -_processor_core_count :: proc() -> int { - return 1 -} - -@(require_results) -file_size :: proc(fd: Handle) -> (size: i64, err: Errno) { - stat := wasi.fd_filestat_get(wasi.fd_t(fd)) or_return - size = i64(stat.size) - return -} - - -exit :: proc "contextless" (code: int) -> ! { - runtime._cleanup_runtime_contextless() - wasi.proc_exit(wasi.exitcode_t(code)) -} - -@(require_results) -lookup_env_alloc :: proc(key: string, allocator := context.allocator) -> (value: string, found: bool) { - return "", false -} - -@(require_results) -lookup_env_buffer :: proc(buf: []u8, key: string) -> (value: string, err: Error) { - return "", .Env_Var_Not_Found -} -lookup_env :: proc{lookup_env_alloc, lookup_env_buffer} - -@(require_results) -get_env_alloc :: proc(key: string, allocator := context.allocator) -> (value: string) { - value, _ = lookup_env(key, allocator) - return -} - -@(require_results) -get_env_buf :: proc(buf: []u8, key: string) -> (value: string) { - value, _ = lookup_env(buf, key) - return -} -get_env :: proc{get_env_alloc, get_env_buf} \ No newline at end of file diff --git a/core/os/old/os_windows.odin b/core/os/old/os_windows.odin deleted file mode 100644 index 8081d9726..000000000 --- a/core/os/old/os_windows.odin +++ /dev/null @@ -1,871 +0,0 @@ -#+build windows -package os_old - -import win32 "core:sys/windows" -import "base:runtime" -import "base:intrinsics" -import "core:unicode/utf16" - -Handle :: distinct uintptr -File_Time :: distinct u64 - -INVALID_HANDLE :: ~Handle(0) - -O_RDONLY :: 0x00000 -O_WRONLY :: 0x00001 -O_RDWR :: 0x00002 -O_CREATE :: 0x00040 -O_EXCL :: 0x00080 -O_NOCTTY :: 0x00100 -O_TRUNC :: 0x00200 -O_NONBLOCK :: 0x00800 -O_APPEND :: 0x00400 -O_SYNC :: 0x01000 -O_ASYNC :: 0x02000 -O_CLOEXEC :: 0x80000 - -_Platform_Error :: win32.System_Error - -ERROR_FILE_NOT_FOUND :: _Platform_Error(2) -ERROR_PATH_NOT_FOUND :: _Platform_Error(3) -ERROR_ACCESS_DENIED :: _Platform_Error(5) -ERROR_INVALID_HANDLE :: _Platform_Error(6) -ERROR_NOT_ENOUGH_MEMORY :: _Platform_Error(8) -ERROR_NO_MORE_FILES :: _Platform_Error(18) -ERROR_HANDLE_EOF :: _Platform_Error(38) -ERROR_NETNAME_DELETED :: _Platform_Error(64) -ERROR_FILE_EXISTS :: _Platform_Error(80) -ERROR_INVALID_PARAMETER :: _Platform_Error(87) -ERROR_BROKEN_PIPE :: _Platform_Error(109) -ERROR_BUFFER_OVERFLOW :: _Platform_Error(111) -ERROR_INSUFFICIENT_BUFFER :: _Platform_Error(122) -ERROR_MOD_NOT_FOUND :: _Platform_Error(126) -ERROR_PROC_NOT_FOUND :: _Platform_Error(127) -ERROR_NEGATIVE_SEEK :: _Platform_Error(131) -ERROR_DIR_NOT_EMPTY :: _Platform_Error(145) -ERROR_ALREADY_EXISTS :: _Platform_Error(183) -ERROR_ENVVAR_NOT_FOUND :: _Platform_Error(203) -ERROR_MORE_DATA :: _Platform_Error(234) -ERROR_OPERATION_ABORTED :: _Platform_Error(995) -ERROR_IO_PENDING :: _Platform_Error(997) -ERROR_NOT_FOUND :: _Platform_Error(1168) -ERROR_PRIVILEGE_NOT_HELD :: _Platform_Error(1314) -WSAEACCES :: _Platform_Error(10013) -WSAECONNRESET :: _Platform_Error(10054) - -ERROR_FILE_IS_PIPE :: General_Error.File_Is_Pipe -ERROR_FILE_IS_NOT_DIR :: General_Error.Not_Dir - -// "Argv" arguments converted to Odin strings -args := _alloc_command_line_arguments() - -@(require_results, no_instrumentation) -get_last_error :: proc "contextless" () -> Error { - err := win32.GetLastError() - if err == 0 { - return nil - } - switch err { - case win32.ERROR_ACCESS_DENIED, win32.ERROR_SHARING_VIOLATION: - return .Permission_Denied - - case win32.ERROR_FILE_EXISTS, win32.ERROR_ALREADY_EXISTS: - return .Exist - - case win32.ERROR_FILE_NOT_FOUND, win32.ERROR_PATH_NOT_FOUND: - return .Not_Exist - - case win32.ERROR_NO_DATA: - return .Closed - - case win32.ERROR_TIMEOUT, win32.WAIT_TIMEOUT: - return .Timeout - - case win32.ERROR_NOT_SUPPORTED: - return .Unsupported - - case win32.ERROR_HANDLE_EOF: - return .EOF - - case win32.ERROR_INVALID_HANDLE: - return .Invalid_File - - case win32.ERROR_NEGATIVE_SEEK: - return .Invalid_Offset - - case - win32.ERROR_BAD_ARGUMENTS, - win32.ERROR_INVALID_PARAMETER, - win32.ERROR_NOT_ENOUGH_MEMORY, - win32.ERROR_NO_MORE_FILES, - win32.ERROR_LOCK_VIOLATION, - win32.ERROR_BROKEN_PIPE, - win32.ERROR_CALL_NOT_IMPLEMENTED, - win32.ERROR_INSUFFICIENT_BUFFER, - win32.ERROR_INVALID_NAME, - win32.ERROR_LOCK_FAILED, - win32.ERROR_ENVVAR_NOT_FOUND, - win32.ERROR_OPERATION_ABORTED, - win32.ERROR_IO_PENDING, - win32.ERROR_NO_UNICODE_TRANSLATION: - // fallthrough - } - return Platform_Error(err) -} - - -@(require_results) -last_write_time :: proc(fd: Handle) -> (File_Time, Error) { - file_info: win32.BY_HANDLE_FILE_INFORMATION - if !win32.GetFileInformationByHandle(win32.HANDLE(fd), &file_info) { - return 0, get_last_error() - } - lo := File_Time(file_info.ftLastWriteTime.dwLowDateTime) - hi := File_Time(file_info.ftLastWriteTime.dwHighDateTime) - return lo | hi << 32, nil -} - -@(require_results) -last_write_time_by_name :: proc(name: string) -> (File_Time, Error) { - data: win32.WIN32_FILE_ATTRIBUTE_DATA - - wide_path := win32.utf8_to_wstring(name) - if !win32.GetFileAttributesExW(wide_path, win32.GetFileExInfoStandard, &data) { - return 0, get_last_error() - } - - l := File_Time(data.ftLastWriteTime.dwLowDateTime) - h := File_Time(data.ftLastWriteTime.dwHighDateTime) - return l | h << 32, nil -} - - -@(require_results) -get_page_size :: proc() -> int { - // NOTE(tetra): The page size never changes, so why do anything complicated - // if we don't have to. - @static page_size := -1 - if page_size != -1 { - return page_size - } - - info: win32.SYSTEM_INFO - win32.GetSystemInfo(&info) - page_size = int(info.dwPageSize) - return page_size -} - -@(private, require_results) -_processor_core_count :: proc() -> int { - length : win32.DWORD = 0 - result := win32.GetLogicalProcessorInformation(nil, &length) - - thread_count := 0 - if !result && win32.GetLastError() == 122 && length > 0 { - runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD() - processors := make([]win32.SYSTEM_LOGICAL_PROCESSOR_INFORMATION, length, context.temp_allocator) - - result = win32.GetLogicalProcessorInformation(&processors[0], &length) - if result { - for processor in processors { - if processor.Relationship == .RelationProcessorCore { - thread := intrinsics.count_ones(processor.ProcessorMask) - thread_count += int(thread) - } - } - } - } - - return thread_count -} - -exit :: proc "contextless" (code: int) -> ! { - runtime._cleanup_runtime_contextless() - win32.ExitProcess(win32.DWORD(code)) -} - - - -@(require_results) -current_thread_id :: proc "contextless" () -> int { - return int(win32.GetCurrentThreadId()) -} - - - -@(private, require_results) -_alloc_command_line_arguments :: proc "contextless" () -> []string { - context = runtime.default_context() - arg_count: i32 - arg_list_ptr := win32.CommandLineToArgvW(win32.GetCommandLineW(), &arg_count) - arg_list := make([]string, int(arg_count)) - for _, i in arg_list { - wc_str := (^win32.wstring)(uintptr(arg_list_ptr) + size_of(win32.wstring)*uintptr(i))^ - olen := win32.WideCharToMultiByte(win32.CP_UTF8, 0, wc_str, -1, - nil, 0, nil, nil) - - buf := make([]byte, int(olen)) - n := win32.WideCharToMultiByte(win32.CP_UTF8, 0, wc_str, -1, - raw_data(buf), olen, nil, nil) - if n > 0 { - n -= 1 - } - arg_list[i] = string(buf[:n]) - } - - return arg_list -} - -@(private, fini) -_delete_command_line_arguments :: proc "contextless" () { - context = runtime.default_context() - for s in args { - delete(s) - } - delete(args) -} - -/* - Windows 11 (preview) has the same major and minor version numbers - as Windows 10: 10 and 0 respectively. - - To determine if you're on Windows 10 or 11, we need to look at - the build number. As far as we can tell right now, the cutoff is build 22_000. - - TODO: Narrow down this range once Win 11 is published and the last Win 10 builds - become available. -*/ -WINDOWS_11_BUILD_CUTOFF :: 22_000 - -@(require_results) -get_windows_version_w :: proc "contextless" () -> win32.OSVERSIONINFOEXW { - osvi : win32.OSVERSIONINFOEXW - osvi.dwOSVersionInfoSize = size_of(win32.OSVERSIONINFOEXW) - win32.RtlGetVersion(&osvi) - return osvi -} - -@(require_results) -is_windows_xp :: proc "contextless" () -> bool { - osvi := get_windows_version_w() - return (osvi.dwMajorVersion == 5 && osvi.dwMinorVersion == 1) -} - -@(require_results) -is_windows_vista :: proc "contextless" () -> bool { - osvi := get_windows_version_w() - return (osvi.dwMajorVersion == 6 && osvi.dwMinorVersion == 0) -} - -@(require_results) -is_windows_7 :: proc "contextless" () -> bool { - osvi := get_windows_version_w() - return (osvi.dwMajorVersion == 6 && osvi.dwMinorVersion == 1) -} - -@(require_results) -is_windows_8 :: proc "contextless" () -> bool { - osvi := get_windows_version_w() - return (osvi.dwMajorVersion == 6 && osvi.dwMinorVersion == 2) -} - -@(require_results) -is_windows_8_1 :: proc "contextless" () -> bool { - osvi := get_windows_version_w() - return (osvi.dwMajorVersion == 6 && osvi.dwMinorVersion == 3) -} - -@(require_results) -is_windows_10 :: proc "contextless" () -> bool { - osvi := get_windows_version_w() - return (osvi.dwMajorVersion == 10 && osvi.dwMinorVersion == 0 && osvi.dwBuildNumber < WINDOWS_11_BUILD_CUTOFF) -} - -@(require_results) -is_windows_11 :: proc "contextless" () -> bool { - osvi := get_windows_version_w() - return (osvi.dwMajorVersion == 10 && osvi.dwMinorVersion == 0 && osvi.dwBuildNumber >= WINDOWS_11_BUILD_CUTOFF) -} - -@(require_results) -is_path_separator :: proc(c: byte) -> bool { - return c == '/' || c == '\\' -} - -@(require_results) -open :: proc(path: string, mode: int = O_RDONLY, perm: int = 0) -> (Handle, Error) { - if len(path) == 0 { - return INVALID_HANDLE, General_Error.Not_Exist - } - - access: u32 - switch mode & (O_RDONLY|O_WRONLY|O_RDWR) { - case O_RDONLY: access = win32.FILE_GENERIC_READ - case O_WRONLY: access = win32.FILE_GENERIC_WRITE - case O_RDWR: access = win32.FILE_GENERIC_READ | win32.FILE_GENERIC_WRITE - } - - if mode&O_CREATE != 0 { - access |= win32.FILE_GENERIC_WRITE - } - if mode&O_APPEND != 0 { - access &~= win32.FILE_GENERIC_WRITE - access |= win32.FILE_APPEND_DATA - } - - share_mode := win32.FILE_SHARE_READ|win32.FILE_SHARE_WRITE - sa: ^win32.SECURITY_ATTRIBUTES = nil - sa_inherit := win32.SECURITY_ATTRIBUTES{nLength = size_of(win32.SECURITY_ATTRIBUTES), bInheritHandle = true} - if mode&O_CLOEXEC == 0 { - sa = &sa_inherit - } - - create_mode: u32 - switch { - case mode&(O_CREATE|O_EXCL) == (O_CREATE | O_EXCL): - create_mode = win32.CREATE_NEW - case mode&(O_CREATE|O_TRUNC) == (O_CREATE | O_TRUNC): - create_mode = win32.CREATE_ALWAYS - case mode&O_CREATE == O_CREATE: - create_mode = win32.OPEN_ALWAYS - case mode&O_TRUNC == O_TRUNC: - create_mode = win32.TRUNCATE_EXISTING - case: - create_mode = win32.OPEN_EXISTING - } - - attrs := win32.FILE_ATTRIBUTE_NORMAL|win32.FILE_FLAG_BACKUP_SEMANTICS - if mode & (O_NONBLOCK) == O_NONBLOCK { - attrs |= win32.FILE_FLAG_OVERLAPPED - } - - wide_path := win32.utf8_to_wstring(path) - handle := Handle(win32.CreateFileW(wide_path, access, share_mode, sa, create_mode, attrs, nil)) - if handle != INVALID_HANDLE { - return handle, nil - } - - return INVALID_HANDLE, get_last_error() -} - -close :: proc(fd: Handle) -> Error { - if !win32.CloseHandle(win32.HANDLE(fd)) { - return get_last_error() - } - return nil -} - -flush :: proc(fd: Handle) -> (err: Error) { - if !win32.FlushFileBuffers(win32.HANDLE(fd)) { - err = get_last_error() - } - return -} - - - -write :: proc(fd: Handle, data: []byte) -> (int, Error) { - if len(data) == 0 { - return 0, nil - } - - single_write_length: win32.DWORD - total_write: i64 - length := i64(len(data)) - - for total_write < length { - remaining := length - total_write - to_write := win32.DWORD(min(i32(remaining), MAX_RW)) - - e := win32.WriteFile(win32.HANDLE(fd), &data[total_write], to_write, &single_write_length, nil) - if single_write_length <= 0 || !e { - return int(total_write), get_last_error() - } - total_write += i64(single_write_length) - } - return int(total_write), nil -} - -@(private="file", require_results) -read_console :: proc(handle: win32.HANDLE, b: []byte) -> (n: int, err: Error) { - if len(b) == 0 { - return 0, nil - } - - BUF_SIZE :: 386 - buf16: [BUF_SIZE]u16 - buf8: [4*BUF_SIZE]u8 - - for n < len(b) && err == nil { - min_read := max(len(b)/4, 1 if len(b) > 0 else 0) - max_read := u32(min(BUF_SIZE, min_read)) - if max_read == 0 { - break - } - - single_read_length: u32 - ok := win32.ReadConsoleW(handle, &buf16[0], max_read, &single_read_length, nil) - if !ok { - err = get_last_error() - } - - buf8_len := utf16.decode_to_utf8(buf8[:], buf16[:single_read_length]) - src := buf8[:buf8_len] - - ctrl_z := false - for i := 0; i < len(src) && n < len(b); i += 1 { - x := src[i] - if x == 0x1a { // ctrl-z - ctrl_z = true - break - } - b[n] = x - n += 1 - } - if ctrl_z || single_read_length < max_read { - break - } - - // NOTE(bill): if the last two values were a newline, then it is expected that - // this is the end of the input - if n >= 2 && single_read_length == max_read && string(b[n-2:n]) == "\r\n" { - break - } - - } - - return -} - -read :: proc(fd: Handle, data: []byte) -> (total_read: int, err: Error) { - if len(data) == 0 { - return 0, nil - } - - handle := win32.HANDLE(fd) - - m: u32 - is_console := win32.GetConsoleMode(handle, &m) - length := len(data) - - // NOTE(Jeroen): `length` can't be casted to win32.DWORD here because it'll overflow if > 4 GiB and return 0 if exactly that. - to_read := min(i64(length), MAX_RW) - - if is_console { - total_read, err = read_console(handle, data[total_read:][:to_read]) - if err != nil { - return total_read, err - } - } else { - // NOTE(Jeroen): So we cast it here *after* we've ensured that `to_read` is at most MAX_RW (1 GiB) - bytes_read: win32.DWORD - if e := win32.ReadFile(handle, &data[total_read], win32.DWORD(to_read), &bytes_read, nil); e { - // Successful read can mean two things, including EOF, see: - // https://learn.microsoft.com/en-us/windows/win32/fileio/testing-for-the-end-of-a-file - if bytes_read == 0 { - return 0, .EOF - } else { - return int(bytes_read), nil - } - } else { - return 0, get_last_error() - } - } - return total_read, nil -} - -seek :: proc(fd: Handle, offset: i64, whence: int) -> (i64, Error) { - w: u32 - switch whence { - case 0: w = win32.FILE_BEGIN - case 1: w = win32.FILE_CURRENT - case 2: w = win32.FILE_END - case: - return 0, .Invalid_Whence - } - hi := i32(offset>>32) - lo := i32(offset) - ft := win32.GetFileType(win32.HANDLE(fd)) - if ft == win32.FILE_TYPE_PIPE { - return 0, .File_Is_Pipe - } - - dw_ptr := win32.SetFilePointer(win32.HANDLE(fd), lo, &hi, w) - if dw_ptr == win32.INVALID_SET_FILE_POINTER { - err := get_last_error() - return 0, err - } - return i64(hi)<<32 + i64(dw_ptr), nil -} - -@(require_results) -file_size :: proc(fd: Handle) -> (i64, Error) { - length: win32.LARGE_INTEGER - err: Error - if !win32.GetFileSizeEx(win32.HANDLE(fd), &length) { - err = get_last_error() - } - return i64(length), err -} - - -@(private) -MAX_RW :: 1<<30 - -@(private) -pread :: proc(fd: Handle, data: []byte, offset: i64) -> (n: int, err: Error) { - curr_off := seek(fd, 0, 1) or_return - defer seek(fd, curr_off, 0) - - buf := data - if len(buf) > MAX_RW { - buf = buf[:MAX_RW] - } - - o := win32.OVERLAPPED{ - OffsetHigh = u32(offset>>32), - Offset = u32(offset), - } - - // TODO(bill): Determine the correct behaviour for consoles - - h := win32.HANDLE(fd) - done: win32.DWORD - e: Error - if !win32.ReadFile(h, raw_data(buf), u32(len(buf)), &done, &o) { - e = get_last_error() - done = 0 - } - return int(done), e -} -@(private) -pwrite :: proc(fd: Handle, data: []byte, offset: i64) -> (n: int, err: Error) { - curr_off := seek(fd, 0, 1) or_return - defer seek(fd, curr_off, 0) - - buf := data - if len(buf) > MAX_RW { - buf = buf[:MAX_RW] - } - - o := win32.OVERLAPPED{ - OffsetHigh = u32(offset>>32), - Offset = u32(offset), - } - - h := win32.HANDLE(fd) - done: win32.DWORD - e: Error - if !win32.WriteFile(h, raw_data(buf), u32(len(buf)), &done, &o) { - e = get_last_error() - done = 0 - } - return int(done), e -} - -/* -read_at returns n: 0, err: 0 on EOF -*/ -read_at :: proc(fd: Handle, data: []byte, offset: i64) -> (n: int, err: Error) { - if offset < 0 { - return 0, .Invalid_Offset - } - - b, offset := data, offset - for len(b) > 0 { - m, e := pread(fd, b, offset) - if e == ERROR_EOF { - err = nil - break - } - if e != nil { - err = e - break - } - n += m - b = b[m:] - offset += i64(m) - } - return -} - -write_at :: proc(fd: Handle, data: []byte, offset: i64) -> (n: int, err: Error) { - if offset < 0 { - return 0, .Invalid_Offset - } - - b, offset := data, offset - for len(b) > 0 { - m := pwrite(fd, b, offset) or_return - n += m - b = b[m:] - offset += i64(m) - } - return -} - - - -// NOTE(bill): Uses startup to initialize it -stdin := get_std_handle(uint(win32.STD_INPUT_HANDLE)) -stdout := get_std_handle(uint(win32.STD_OUTPUT_HANDLE)) -stderr := get_std_handle(uint(win32.STD_ERROR_HANDLE)) - - -@(require_results) -get_std_handle :: proc "contextless" (h: uint) -> Handle { - fd := win32.GetStdHandle(win32.DWORD(h)) - return Handle(fd) -} - - -exists :: proc(path: string) -> bool { - runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD() - wpath := win32.utf8_to_wstring(path, context.temp_allocator) - attribs := win32.GetFileAttributesW(wpath) - - return attribs != win32.INVALID_FILE_ATTRIBUTES -} - -@(require_results) -is_file :: proc(path: string) -> bool { - runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD() - wpath := win32.utf8_to_wstring(path, context.temp_allocator) - attribs := win32.GetFileAttributesW(wpath) - - if attribs != win32.INVALID_FILE_ATTRIBUTES { - return attribs & win32.FILE_ATTRIBUTE_DIRECTORY == 0 - } - return false -} - -@(require_results) -is_dir :: proc(path: string) -> bool { - runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD() - wpath := win32.utf8_to_wstring(path, context.temp_allocator) - attribs := win32.GetFileAttributesW(wpath) - - if attribs != win32.INVALID_FILE_ATTRIBUTES { - return attribs & win32.FILE_ATTRIBUTE_DIRECTORY != 0 - } - return false -} - -// NOTE(tetra): GetCurrentDirectory is not thread safe with SetCurrentDirectory and GetFullPathName -@private cwd_lock := win32.SRWLOCK{} // zero is initialized - -@(require_results) -get_current_directory :: proc(allocator := context.allocator) -> string { - win32.AcquireSRWLockExclusive(&cwd_lock) - - runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD(ignore = context.temp_allocator == allocator) - - sz_utf16 := win32.GetCurrentDirectoryW(0, nil) - dir_buf_wstr, _ := make([]u16, sz_utf16, context.temp_allocator) // the first time, it _includes_ the NUL. - - sz_utf16 = win32.GetCurrentDirectoryW(win32.DWORD(len(dir_buf_wstr)), raw_data(dir_buf_wstr)) - assert(int(sz_utf16)+1 == len(dir_buf_wstr)) // the second time, it _excludes_ the NUL. - - win32.ReleaseSRWLockExclusive(&cwd_lock) - - return win32.utf16_to_utf8(dir_buf_wstr, allocator) or_else "" -} - -set_current_directory :: proc(path: string) -> (err: Error) { - runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD() - wstr := win32.utf8_to_wstring(path, context.temp_allocator) - - win32.AcquireSRWLockExclusive(&cwd_lock) - - if !win32.SetCurrentDirectoryW(wstr) { - err = get_last_error() - } - - win32.ReleaseSRWLockExclusive(&cwd_lock) - - return -} -change_directory :: set_current_directory - -make_directory :: proc(path: string, mode: u32 = 0) -> (err: Error) { - runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD() - // Mode is unused on Windows, but is needed on *nix - wpath := win32.utf8_to_wstring(path, context.temp_allocator) - - if !win32.CreateDirectoryW(wpath, nil) { - err = get_last_error() - } - return -} - - -remove_directory :: proc(path: string) -> (err: Error) { - runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD() - wpath := win32.utf8_to_wstring(path, context.temp_allocator) - - if !win32.RemoveDirectoryW(wpath) { - err = get_last_error() - } - return -} - - - -@(private, require_results) -is_abs :: proc(path: string) -> bool { - if len(path) > 0 && path[0] == '/' { - return true - } - when ODIN_OS == .Windows { - if len(path) > 2 { - switch path[0] { - case 'A'..='Z', 'a'..='z': - return path[1] == ':' && is_path_separator(path[2]) - } - } - } - return false -} - -@(private, require_results) -fix_long_path :: proc(path: string) -> string { - if len(path) < 248 { - return path - } - - if len(path) >= 2 && path[:2] == `\\` { - return path - } - if !is_abs(path) { - return path - } - - prefix :: `\\?` - - path_buf, _ := make([]byte, len(prefix)+len(path)+len(`\`), context.temp_allocator) - copy(path_buf, prefix) - n := len(path) - r, w := 0, len(prefix) - for r < n { - switch { - case is_path_separator(path[r]): - r += 1 - case path[r] == '.' && (r+1 == n || is_path_separator(path[r+1])): - r += 1 - case r+1 < n && path[r] == '.' && path[r+1] == '.' && (r+2 == n || is_path_separator(path[r+2])): - return path - case: - path_buf[w] = '\\' - w += 1 - for ; r < n && !is_path_separator(path[r]); r += 1 { - path_buf[w] = path[r] - w += 1 - } - } - } - - if w == len(`\\?\c:`) { - path_buf[w] = '\\' - w += 1 - } - return string(path_buf[:w]) -} - - -link :: proc(old_name, new_name: string) -> (err: Error) { - runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD() - n := win32.utf8_to_wstring(fix_long_path(new_name)) - o := win32.utf8_to_wstring(fix_long_path(old_name)) - return Platform_Error(win32.CreateHardLinkW(n, o, nil)) -} - -unlink :: proc(path: string) -> (err: Error) { - runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD() - wpath := win32.utf8_to_wstring(path, context.temp_allocator) - - if !win32.DeleteFileW(wpath) { - err = get_last_error() - } - return -} - - - -rename :: proc(old_path, new_path: string) -> (err: Error) { - runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD() - from := win32.utf8_to_wstring(old_path, context.temp_allocator) - to := win32.utf8_to_wstring(new_path, context.temp_allocator) - - if !win32.MoveFileExW(from, to, win32.MOVEFILE_REPLACE_EXISTING) { - err = get_last_error() - } - return -} - - -ftruncate :: proc(fd: Handle, length: i64) -> (err: Error) { - curr_off := seek(fd, 0, 1) or_return - defer seek(fd, curr_off, 0) - _= seek(fd, length, 0) or_return - ok := win32.SetEndOfFile(win32.HANDLE(fd)) - if !ok { - return get_last_error() - } - return nil -} - -truncate :: proc(path: string, length: i64) -> (err: Error) { - fd := open(path, O_WRONLY|O_CREATE, 0o666) or_return - defer close(fd) - return ftruncate(fd, length) -} - - -remove :: proc(name: string) -> Error { - p := win32.utf8_to_wstring(fix_long_path(name)) - err, err1: win32.DWORD - if !win32.DeleteFileW(p) { - err = win32.GetLastError() - } - if err == 0 { - return nil - } - if !win32.RemoveDirectoryW(p) { - err1 = win32.GetLastError() - } - if err1 == 0 { - return nil - } - - if err != err1 { - a := win32.GetFileAttributesW(p) - if a == ~u32(0) { - err = win32.GetLastError() - } else { - if a & win32.FILE_ATTRIBUTE_DIRECTORY != 0 { - err = err1 - } else if a & win32.FILE_ATTRIBUTE_READONLY != 0 { - if win32.SetFileAttributesW(p, a &~ win32.FILE_ATTRIBUTE_READONLY) { - err = 0 - if !win32.DeleteFileW(p) { - err = win32.GetLastError() - } - } - } - } - } - - return Platform_Error(err) -} - - -@(require_results) -pipe :: proc() -> (r, w: Handle, err: Error) { - sa: win32.SECURITY_ATTRIBUTES - sa.nLength = size_of(win32.SECURITY_ATTRIBUTES) - sa.bInheritHandle = true - if !win32.CreatePipe((^win32.HANDLE)(&r), (^win32.HANDLE)(&w), &sa, 0) { - err = get_last_error() - } - return -} diff --git a/core/os/old/stat.odin b/core/os/old/stat.odin deleted file mode 100644 index fad8ff755..000000000 --- a/core/os/old/stat.odin +++ /dev/null @@ -1,33 +0,0 @@ -package os_old - -import "core:time" - -File_Info :: struct { - fullpath: string, // allocated - name: string, // uses `fullpath` as underlying data - size: i64, - mode: File_Mode, - is_dir: bool, - creation_time: time.Time, - modification_time: time.Time, - access_time: time.Time, -} - -file_info_slice_delete :: proc(infos: []File_Info, allocator := context.allocator) { - for i := len(infos)-1; i >= 0; i -= 1 { - file_info_delete(infos[i], allocator) - } - delete(infos, allocator) -} - -file_info_delete :: proc(fi: File_Info, allocator := context.allocator) { - delete(fi.fullpath, allocator) -} - -File_Mode :: distinct u32 - -File_Mode_Dir :: File_Mode(1<<16) -File_Mode_Named_Pipe :: File_Mode(1<<17) -File_Mode_Device :: File_Mode(1<<18) -File_Mode_Char_Device :: File_Mode(1<<19) -File_Mode_Sym_Link :: File_Mode(1<<20) diff --git a/core/os/old/stat_unix.odin b/core/os/old/stat_unix.odin deleted file mode 100644 index d7f49e12b..000000000 --- a/core/os/old/stat_unix.odin +++ /dev/null @@ -1,134 +0,0 @@ -#+build linux, darwin, freebsd, openbsd, netbsd -package os_old - -import "core:time" - -/* -For reference -------------- - -Unix_File_Time :: struct { - seconds: i64, - nanoseconds: i64, -} - -Stat :: struct { - device_id: u64, // ID of device containing file - serial: u64, // File serial number - nlink: u64, // Number of hard links - mode: u32, // Mode of the file - uid: u32, // User ID of the file's owner - gid: u32, // Group ID of the file's group - _padding: i32, // 32 bits of padding - rdev: u64, // Device ID, if device - size: i64, // Size of the file, in bytes - block_size: i64, // Optimal bllocksize for I/O - blocks: i64, // Number of 512-byte blocks allocated - - last_access: Unix_File_Time, // Time of last access - modified: Unix_File_Time, // Time of last modification - status_change: Unix_File_Time, // Time of last status change - - _reserve1, - _reserve2, - _reserve3: i64, -}; - -Time :: struct { - _nsec: i64, // zero is 1970-01-01 00:00:00 -} - -File_Info :: struct { - fullpath: string, - name: string, - size: i64, - mode: File_Mode, - is_dir: bool, - creation_time: time.Time, - modification_time: time.Time, - access_time: time.Time, -} -*/ - -@(private, require_results) -_make_time_from_unix_file_time :: proc(uft: Unix_File_Time) -> time.Time { - return time.Time{ - _nsec = i64(uft.nanoseconds) + i64(uft.seconds) * 1_000_000_000, - } -} - -@(private) -_fill_file_info_from_stat :: proc(fi: ^File_Info, s: OS_Stat) { - fi.size = s.size - fi.mode = cast(File_Mode)s.mode - fi.is_dir = S_ISDIR(s.mode) - - // NOTE(laleksic, 2021-01-21): Not really creation time, but closest we can get (maybe better to leave it 0?) - fi.creation_time = _make_time_from_unix_file_time(s.status_change) - - fi.modification_time = _make_time_from_unix_file_time(s.modified) - fi.access_time = _make_time_from_unix_file_time(s.last_access) -} - - -@(private, require_results) -path_base :: proc(path: string) -> string { - is_separator :: proc(c: byte) -> bool { - return c == '/' - } - - if path == "" { - return "." - } - - path := path - for len(path) > 0 && is_separator(path[len(path)-1]) { - path = path[:len(path)-1] - } - - i := len(path)-1 - for i >= 0 && !is_separator(path[i]) { - i -= 1 - } - if i >= 0 { - path = path[i+1:] - } - if path == "" { - return "/" - } - return path -} - - -@(require_results) -lstat :: proc(name: string, allocator := context.allocator) -> (fi: File_Info, err: Error) { - context.allocator = allocator - - s := _lstat(name) or_return - _fill_file_info_from_stat(&fi, s) - fi.fullpath = absolute_path_from_relative(name) or_return - fi.name = path_base(fi.fullpath) - return -} - -@(require_results) -stat :: proc(name: string, allocator := context.allocator) -> (fi: File_Info, err: Error) { - context.allocator = allocator - - s := _stat(name) or_return - _fill_file_info_from_stat(&fi, s) - fi.fullpath = absolute_path_from_relative(name) or_return - fi.name = path_base(fi.fullpath) - return -} - -@(require_results) -fstat :: proc(fd: Handle, allocator := context.allocator) -> (fi: File_Info, err: Error) { - context.allocator = allocator - - s := _fstat(fd) or_return - _fill_file_info_from_stat(&fi, s) - fi.fullpath = absolute_path_from_handle(fd) or_return - fi.name = path_base(fi.fullpath) - return -} diff --git a/core/os/old/stat_windows.odin b/core/os/old/stat_windows.odin deleted file mode 100644 index 34e5e1695..000000000 --- a/core/os/old/stat_windows.odin +++ /dev/null @@ -1,303 +0,0 @@ -package os_old - -import "core:time" -import "base:runtime" -import win32 "core:sys/windows" - -@(private, require_results) -full_path_from_name :: proc(name: string, allocator := context.allocator) -> (path: string, err: Errno) { - context.allocator = allocator - - name := name - if name == "" { - name = "." - } - runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD(ignore = context.temp_allocator == allocator) - p := win32.utf8_to_utf16(name, context.temp_allocator) - buf := make([dynamic]u16, 100) - defer delete(buf) - for { - n := win32.GetFullPathNameW(cstring16(raw_data(p)), u32(len(buf)), cstring16(raw_data(buf)), nil) - if n == 0 { - return "", get_last_error() - } - if n <= u32(len(buf)) { - return win32.utf16_to_utf8(buf[:n], allocator) or_else "", nil - } - resize(&buf, len(buf)*2) - } - - return -} - -@(private, require_results) -_stat :: proc(name: string, create_file_attributes: u32, allocator := context.allocator) -> (fi: File_Info, e: Errno) { - if len(name) == 0 { - return {}, ERROR_PATH_NOT_FOUND - } - - context.allocator = allocator - - runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD(ignore = context.temp_allocator == allocator) - - wname := win32.utf8_to_wstring(fix_long_path(name), context.temp_allocator) - fa: win32.WIN32_FILE_ATTRIBUTE_DATA - ok := win32.GetFileAttributesExW(wname, win32.GetFileExInfoStandard, &fa) - if ok && fa.dwFileAttributes & win32.FILE_ATTRIBUTE_REPARSE_POINT == 0 { - // Not a symlink - return file_info_from_win32_file_attribute_data(&fa, name) - } - - err := 0 if ok else win32.GetLastError() - - if err == win32.ERROR_SHARING_VIOLATION { - fd: win32.WIN32_FIND_DATAW - sh := win32.FindFirstFileW(wname, &fd) - if sh == win32.INVALID_HANDLE_VALUE { - e = get_last_error() - return - } - win32.FindClose(sh) - - return file_info_from_win32_find_data(&fd, name) - } - - h := win32.CreateFileW(wname, 0, 0, nil, win32.OPEN_EXISTING, create_file_attributes, nil) - if h == win32.INVALID_HANDLE_VALUE { - e = get_last_error() - return - } - defer win32.CloseHandle(h) - return file_info_from_get_file_information_by_handle(name, h) -} - - -@(require_results) -lstat :: proc(name: string, allocator := context.allocator) -> (File_Info, Errno) { - attrs := win32.FILE_FLAG_BACKUP_SEMANTICS - attrs |= win32.FILE_FLAG_OPEN_REPARSE_POINT - return _stat(name, attrs, allocator) -} - -@(require_results) -stat :: proc(name: string, allocator := context.allocator) -> (File_Info, Errno) { - attrs := win32.FILE_FLAG_BACKUP_SEMANTICS - return _stat(name, attrs, allocator) -} - -@(require_results) -fstat :: proc(fd: Handle, allocator := context.allocator) -> (fi: File_Info, err: Errno) { - if fd == 0 { - err = ERROR_INVALID_HANDLE - } - context.allocator = allocator - - path := cleanpath_from_handle(fd) or_return - defer if err != nil { - delete(path) - } - - h := win32.HANDLE(fd) - switch win32.GetFileType(h) { - case win32.FILE_TYPE_PIPE, win32.FILE_TYPE_CHAR: - fi.name = basename(path) - fi.mode |= file_type_mode(h) - err = nil - case: - fi = file_info_from_get_file_information_by_handle(path, h) or_return - } - fi.fullpath = path - return -} - - -@(private, require_results) -cleanpath_strip_prefix :: proc(buf: []u16) -> []u16 { - buf := buf - N := 0 - for c, i in buf { - if c == 0 { break } - N = i+1 - } - buf = buf[:N] - - if len(buf) >= 4 && buf[0] == '\\' && buf[1] == '\\' && buf[2] == '?' && buf[3] == '\\' { - buf = buf[4:] - - /* - NOTE(Jeroen): Properly handle UNC paths. - We need to turn `\\?\UNC\synology.local` into `\\synology.local`. - */ - if len(buf) >= 3 && buf[0] == 'U' && buf[1] == 'N' && buf[2] == 'C' { - buf = buf[2:] - buf[0] = '\\' - } - } - return buf -} - -@(private, require_results) -cleanpath_from_handle :: proc(fd: Handle) -> (s: string, err: Errno) { - runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD(ignore = context.temp_allocator == context.allocator) - buf := cleanpath_from_handle_u16(fd, context.temp_allocator) or_return - return win32.utf16_to_utf8(buf, context.allocator) -} -@(private, require_results) -cleanpath_from_handle_u16 :: proc(fd: Handle, allocator: runtime.Allocator) -> ([]u16, Errno) { - if fd == 0 { - return nil, ERROR_INVALID_HANDLE - } - h := win32.HANDLE(fd) - - n := win32.GetFinalPathNameByHandleW(h, nil, 0, 0) - if n == 0 { - return nil, get_last_error() - } - buf := make([]u16, max(n, win32.DWORD(260))+1, allocator) - buf_len := win32.GetFinalPathNameByHandleW(h, cstring16(raw_data(buf)), n, 0) - return buf[:buf_len], nil -} -@(private, require_results) -cleanpath_from_buf :: proc(buf: []u16) -> string { - buf := buf - buf = cleanpath_strip_prefix(buf) - return win32.utf16_to_utf8(buf, context.allocator) or_else "" -} - -@(private, require_results) -basename :: proc(name: string) -> (base: string) { - name := name - if len(name) > 3 && name[:3] == `\\?` { - name = name[3:] - } - - if len(name) == 2 && name[1] == ':' { - return "." - } else if len(name) > 2 && name[1] == ':' { - name = name[2:] - } - i := len(name)-1 - - for ; i > 0 && (name[i] == '/' || name[i] == '\\'); i -= 1 { - name = name[:i] - } - for i -= 1; i >= 0; i -= 1 { - if name[i] == '/' || name[i] == '\\' { - name = name[i+1:] - break - } - } - return name -} - -@(private, require_results) -file_type_mode :: proc(h: win32.HANDLE) -> File_Mode { - switch win32.GetFileType(h) { - case win32.FILE_TYPE_PIPE: - return File_Mode_Named_Pipe - case win32.FILE_TYPE_CHAR: - return File_Mode_Device | File_Mode_Char_Device - } - return 0 -} - - -@(private, require_results) -file_mode_from_file_attributes :: proc(FileAttributes: win32.DWORD, h: win32.HANDLE, ReparseTag: win32.DWORD) -> (mode: File_Mode) { - if FileAttributes & win32.FILE_ATTRIBUTE_READONLY != 0 { - mode |= 0o444 - } else { - mode |= 0o666 - } - - is_sym := false - if FileAttributes & win32.FILE_ATTRIBUTE_REPARSE_POINT == 0 { - is_sym = false - } else { - is_sym = ReparseTag == win32.IO_REPARSE_TAG_SYMLINK || ReparseTag == win32.IO_REPARSE_TAG_MOUNT_POINT - } - - if is_sym { - mode |= File_Mode_Sym_Link - } else { - if FileAttributes & win32.FILE_ATTRIBUTE_DIRECTORY != 0 { - mode |= 0o111 | File_Mode_Dir - } - - if h != nil { - mode |= file_type_mode(h) - } - } - - return -} - -@(private) -windows_set_file_info_times :: proc(fi: ^File_Info, d: ^$T) { - 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)) -} - -@(private, require_results) -file_info_from_win32_file_attribute_data :: proc(d: ^win32.WIN32_FILE_ATTRIBUTE_DATA, name: string) -> (fi: File_Info, e: Errno) { - fi.size = i64(d.nFileSizeHigh)<<32 + i64(d.nFileSizeLow) - - fi.mode |= file_mode_from_file_attributes(d.dwFileAttributes, nil, 0) - fi.is_dir = fi.mode & File_Mode_Dir != 0 - - windows_set_file_info_times(&fi, d) - - fi.fullpath, e = full_path_from_name(name) - fi.name = basename(fi.fullpath) - - return -} - -@(private, require_results) -file_info_from_win32_find_data :: proc(d: ^win32.WIN32_FIND_DATAW, name: string) -> (fi: File_Info, e: Errno) { - fi.size = i64(d.nFileSizeHigh)<<32 + i64(d.nFileSizeLow) - - fi.mode |= file_mode_from_file_attributes(d.dwFileAttributes, nil, 0) - fi.is_dir = fi.mode & File_Mode_Dir != 0 - - windows_set_file_info_times(&fi, d) - - fi.fullpath, e = full_path_from_name(name) - fi.name = basename(fi.fullpath) - - return -} - -@(private, require_results) -file_info_from_get_file_information_by_handle :: proc(path: string, h: win32.HANDLE) -> (File_Info, Errno) { - d: win32.BY_HANDLE_FILE_INFORMATION - if !win32.GetFileInformationByHandle(h, &d) { - err := get_last_error() - return {}, err - - } - - ti: win32.FILE_ATTRIBUTE_TAG_INFO - if !win32.GetFileInformationByHandleEx(h, .FileAttributeTagInfo, &ti, size_of(ti)) { - err := get_last_error() - if err != ERROR_INVALID_PARAMETER { - return {}, err - } - // Indicate this is a symlink on FAT file systems - ti.ReparseTag = 0 - } - - fi: File_Info - - fi.fullpath = path - fi.name = basename(path) - fi.size = i64(d.nFileSizeHigh)<<32 + i64(d.nFileSizeLow) - - fi.mode |= file_mode_from_file_attributes(ti.FileAttributes, h, ti.ReparseTag) - fi.is_dir = fi.mode & File_Mode_Dir != 0 - - windows_set_file_info_times(&fi, &d) - - return fi, nil -} diff --git a/core/os/old/stream.odin b/core/os/old/stream.odin deleted file mode 100644 index d94505505..000000000 --- a/core/os/old/stream.odin +++ /dev/null @@ -1,77 +0,0 @@ -package os_old - -import "core:io" - -stream_from_handle :: proc(fd: Handle) -> io.Stream { - s: io.Stream - s.data = rawptr(uintptr(fd)) - s.procedure = _file_stream_proc - return s -} - - -@(private) -_file_stream_proc :: proc(stream_data: rawptr, mode: io.Stream_Mode, p: []byte, offset: i64, whence: io.Seek_From) -> (n: i64, err: io.Error) { - fd := Handle(uintptr(stream_data)) - n_int: int - os_err: Error - switch mode { - case .Close: - os_err = close(fd) - case .Flush: - os_err = flush(fd) - case .Read: - if len(p) == 0 { - return 0, nil - } - n_int, os_err = read(fd, p) - n = i64(n_int) - if n == 0 && os_err == nil { - err = .EOF - } - - case .Read_At: - if len(p) == 0 { - return 0, nil - } - n_int, os_err = read_at(fd, p, offset) - n = i64(n_int) - if n == 0 && os_err == nil { - err = .EOF - } - case .Write: - if len(p) == 0 { - return 0, nil - } - n_int, os_err = write(fd, p) - n = i64(n_int) - if n == 0 && os_err == nil { - err = .EOF - } - case .Write_At: - if len(p) == 0 { - return 0, nil - } - n_int, os_err = write_at(fd, p, offset) - n = i64(n_int) - if n == 0 && os_err == nil { - err = .EOF - } - case .Seek: - n, os_err = seek(fd, offset, int(whence)) - case .Size: - n, os_err = file_size(fd) - case .Destroy: - err = .Unsupported - case .Query: - return io.query_utility({.Close, .Flush, .Read, .Read_At, .Write, .Write_At, .Seek, .Size, .Query}) - } - - if err == nil && os_err != nil { - err = error_to_io_error(os_err) - } - if err != nil { - n = 0 - } - return -}