diff options
| author | gingerBill <bill@gingerbill.org> | 2021-04-14 19:39:12 +0100 |
|---|---|---|
| committer | gingerBill <bill@gingerbill.org> | 2021-04-14 19:39:12 +0100 |
| commit | ebbc33fdb5e044e5feb010d6d3a8bde41f71a05f (patch) | |
| tree | 29ccc30a22ef8082df00eb3f2dbcfb9ee73e789f | |
| parent | 3a4373641b68019149007f04a201965ee961f74e (diff) | |
Mockup of the new `package os` interface (incomplete and non-functioning)
| -rw-r--r-- | core/os/os2/doc.odin | 11 | ||||
| -rw-r--r-- | core/os/os2/env.odin | 43 | ||||
| -rw-r--r-- | core/os/os2/env_windows.odin | 80 | ||||
| -rw-r--r-- | core/os/os2/errors.odin | 126 | ||||
| -rw-r--r-- | core/os/os2/errors_windows.odin | 14 | ||||
| -rw-r--r-- | core/os/os2/file.odin | 158 | ||||
| -rw-r--r-- | core/os/os2/file_stream.odin | 98 | ||||
| -rw-r--r-- | core/os/os2/file_util.odin | 122 | ||||
| -rw-r--r-- | core/os/os2/file_windows.odin | 136 | ||||
| -rw-r--r-- | core/os/os2/heap.odin | 21 | ||||
| -rw-r--r-- | core/os/os2/heap_windows.odin | 107 | ||||
| -rw-r--r-- | core/os/os2/path.odin | 29 | ||||
| -rw-r--r-- | core/os/os2/path_windows.odin | 31 | ||||
| -rw-r--r-- | core/os/os2/pipe.odin | 5 | ||||
| -rw-r--r-- | core/os/os2/pipe_windows.odin | 13 | ||||
| -rw-r--r-- | core/os/os2/process.odin | 101 | ||||
| -rw-r--r-- | core/os/os2/stat.odin | 42 | ||||
| -rw-r--r-- | core/os/os2/stat_windows.odin | 373 | ||||
| -rw-r--r-- | core/os/os2/temp_file.odin | 14 | ||||
| -rw-r--r-- | core/os/os2/temp_file_windows.odin | 29 | ||||
| -rw-r--r-- | core/os/os2/user.odin | 68 |
21 files changed, 1621 insertions, 0 deletions
diff --git a/core/os/os2/doc.odin b/core/os/os2/doc.odin new file mode 100644 index 000000000..e413ef186 --- /dev/null +++ b/core/os/os2/doc.odin @@ -0,0 +1,11 @@ +// Package os provides a platform-independent interface to operating system functionality. +// The design is UNIX-like but with Odin-like error handling. Failing calls return values with a specific error type rather than error number. +// +// The package os interface is intended to be uniform across all operating systems. +// Features not generally available appear in the system-specific packages under core:sys/*. +// +// +// IMPORTANT NOTE from Bill: this is purely a mockup of what I want the new package os to be, and NON-FUNCTIONING. +// It is not complete but should give designers a better idea of the general interface and how to write things. +// This entire interface is subject to change. +package os2 diff --git a/core/os/os2/env.odin b/core/os/os2/env.odin new file mode 100644 index 000000000..ae1752a10 --- /dev/null +++ b/core/os/os2/env.odin @@ -0,0 +1,43 @@ +package os2 + +// 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 +get_env :: proc(key: string, allocator := context.allocator) -> string { + value, _ := lookup_env(key, allocator); + return value; +} + +// 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 +lookup_env :: proc(key: string, allocator := context.allocator) -> (value: string, found: bool) { + return _lookup_env(key, allocator); +} + +// set_env sets the value of the environment variable named by the key +// Returns true on success, false on failure +set_env :: proc(key, value: string) -> bool { + return _set_env(key, value); +} + +// unset_env unsets a single environment variable +// Returns true on success, false on failure +unset_env :: proc(key: string) -> bool { + return _unset_env(key); +} + +clear_env :: proc() { + _clear_env(); +} + + +// 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 +environ :: proc(allocator := context.allocator) -> []string { + return _environ(allocator); +} + + diff --git a/core/os/os2/env_windows.odin b/core/os/os2/env_windows.odin new file mode 100644 index 000000000..b6a73ad81 --- /dev/null +++ b/core/os/os2/env_windows.odin @@ -0,0 +1,80 @@ +//+private +package os2 + +import "core:mem" +import win32 "core:sys/windows" + +_lookup_env :: proc(key: string, allocator := context.allocator) -> (value: string, found: bool) { + if key == "" { + return; + } + wkey := win32.utf8_to_wstring(key); + b := make([dynamic]u16, 100, context.temp_allocator); + for { + n := win32.GetEnvironmentVariableW(wkey, raw_data(b), u32(len(b))); + if n == 0 { + err := win32.GetLastError(); + if err == win32.ERROR_ENVVAR_NOT_FOUND { + return "", false; + } + } + + if n <= u32(len(b)) { + value = win32.utf16_to_utf8(b[:n], allocator); + found = true; + return; + } + + resize(&b, len(b)*2); + } +} + +_set_env :: proc(key, value: string) -> bool { + k := win32.utf8_to_wstring(key); + v := win32.utf8_to_wstring(value); + + return bool(win32.SetEnvironmentVariableW(k, v)); +} + +_unset_env :: proc(key: string) -> bool { + k := win32.utf8_to_wstring(key); + return bool(win32.SetEnvironmentVariableW(k, nil)); +} + +_clear_env :: proc() { + envs := environ(context.temp_allocator); + for env in envs { + for j in 1..<len(env) { + if env[j] == '=' { + unset_env(env[0:j]); + break; + } + } + } +} + +_environ :: proc(allocator := context.allocator) -> []string { + envs := win32.GetEnvironmentStringsW(); + if envs == nil { + return nil; + } + defer win32.FreeEnvironmentStringsW(envs); + + r := make([dynamic]string, 0, 50, allocator); + for from, i, p := 0, 0, envs; true; i += 1 { + c := (^u16)(uintptr(p) + uintptr(i*2))^; + if c == 0 { + if i <= from { + break; + } + w := mem.slice_ptr(mem.ptr_offset(p, from), i-from); + + append(&r, win32.utf16_to_utf8(w, allocator)); + from = i + 1; + } + } + + return r[:]; +} + + diff --git a/core/os/os2/errors.odin b/core/os/os2/errors.odin new file mode 100644 index 000000000..00cd600a8 --- /dev/null +++ b/core/os/os2/errors.odin @@ -0,0 +1,126 @@ +package os2 + +Platform_Error_Min_Bits :: 32; + +Error :: enum u64 { + None = 0, + + // General Errors + Invalid_Argument, + + Permission_Denied, + Exist, + Not_Exist, + Closed, + + // Timeout Errors + Timeout, + + // I/O Errors + // EOF is the error returned by `read` when no more input is available + EOF, + + // Unexpected_EOF means that EOF was encountered in the middle of reading a fixed-sized block of data + Unexpected_EOF, + + // Short_Write means that a write accepted fewer bytes than requested but failed to return an explicit error + Short_Write, + + // Invalid_Write means that a write returned an impossible count + Invalid_Write, + + // Short_Buffer means that a read required a longer buffer than was provided + Short_Buffer, + + // No_Progress is returned by some implementations of `io.Reader` when many calls + // to `read` have failed to return any data or error. + // This is usually a signed of a broken `io.Reader` implementation + No_Progress, + + Invalid_Whence, + Invalid_Offset, + Invalid_Unread, + + Negative_Read, + Negative_Write, + Negative_Count, + Buffer_Full, + + // Platform Specific Errors + Platform_Minimum = 1<<Platform_Error_Min_Bits, +} + +Path_Error :: struct { + op: string, + path: string, + err: Error, +} + +Link_Error :: struct { + op: string, + old: string, + new: string, + err: Error, +} + +path_error_delete :: proc(perr: Maybe(Path_Error)) { + if err, ok := perr.?; ok { + context.allocator = error_allocator(); + delete(err.op); + delete(err.path); + } +} + +link_error_delete :: proc(lerr: Maybe(Link_Error)) { + if err, ok := lerr.?; ok { + context.allocator = error_allocator(); + delete(err.op); + delete(err.old); + delete(err.new); + } +} + + + +is_platform_error :: proc(ferr: Error) -> (err: i32, ok: bool) { + if ferr >= .Platform_Minimum { + err = i32(u64(ferr)>>Platform_Error_Min_Bits); + ok = true; + } + return; +} + +error_from_platform_error :: proc(errno: i32) -> Error { + return Error(u64(errno) << Platform_Error_Min_Bits); +} + +error_string :: proc(ferr: Error) -> string { + #partial switch ferr { + case .None: return ""; + case .Invalid_Argument: return "invalid argument"; + case .Permission_Denied: return "permission denied"; + case .Exist: return "file already exists"; + case .Not_Exist: return "file does not exist"; + case .Closed: return "file already closed"; + case .Timeout: return "i/o timeout"; + case .EOF: return "eof"; + case .Unexpected_EOF: return "unexpected eof"; + case .Short_Write: return "short write"; + case .Invalid_Write: return "invalid write result"; + case .Short_Buffer: return "short buffer"; + case .No_Progress: return "multiple read calls return no data or error"; + case .Invalid_Whence: return "invalid whence"; + case .Invalid_Offset: return "invalid offset"; + case .Invalid_Unread: return "invalid unread"; + case .Negative_Read: return "negative read"; + case .Negative_Write: return "negative write"; + case .Negative_Count: return "negative count"; + case .Buffer_Full: return "buffer full"; + } + + if errno, ok := is_platform_error(ferr); ok { + return _error_string(errno); + } + + return "unknown error"; +} diff --git a/core/os/os2/errors_windows.odin b/core/os/os2/errors_windows.odin new file mode 100644 index 000000000..18e99b615 --- /dev/null +++ b/core/os/os2/errors_windows.odin @@ -0,0 +1,14 @@ +//+private +package os2 + +import win32 "core:sys/windows" + +_error_string :: proc(errno: i32) -> string { + e := win32.DWORD(errno); + if e == 0 { + return ""; + } + // TODO(bill): _error_string for windows + // FormatMessageW + return ""; +} diff --git a/core/os/os2/file.odin b/core/os/os2/file.odin new file mode 100644 index 000000000..d64855646 --- /dev/null +++ b/core/os/os2/file.odin @@ -0,0 +1,158 @@ +package os2 + +import "core:io" +import "core:time" + +Handle :: distinct uintptr; + +Seek_From :: enum { + Start = 0, // seek relative to the origin of the file + Current = 1, // seek relative to the current offset + End = 2, // seek relative to the end +} + +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); + + +O_RDONLY :: int( 0); +O_WRONLY :: int( 1); +O_RDWR :: int( 2); +O_APPEND :: int( 4); +O_CREATE :: int( 8); +O_EXCL :: int(16); +O_SYNC :: int(32); +O_TRUNC :: int(64); + + + +stdin: Handle = 0; // OS-Specific +stdout: Handle = 1; // OS-Specific +stderr: Handle = 2; // OS-Specific + + +create :: proc(name: string) -> (Handle, Error) { + return _create(name); +} + +open :: proc(name: string) -> (Handle, Error) { + return _open(name); +} + +open_file :: proc(name: string, flag: int, perm: File_Mode) -> (Handle, Error) { + return _open_file(name, flag, perm); +} + +close :: proc(fd: Handle) -> Error { + return _close(fd); +} + +name :: proc(fd: Handle, allocator := context.allocator) -> string { + return _name(fd); +} + +seek :: proc(fd: Handle, offset: i64, whence: Seek_From) -> (ret: i64, err: Error) { + return _seek(fd, offset, whence); +} + +read :: proc(fd: Handle, p: []byte) -> (n: int, err: Error) { + return _read(fd, p); +} + +read_at :: proc(fd: Handle, p: []byte, offset: i64) -> (n: int, err: Error) { + return _read_at(fd, p, offset); +} + +read_from :: proc(fd: Handle, r: io.Reader) -> (n: i64, err: Error) { + return _read_from(fd, r); +} + +write :: proc(fd: Handle, p: []byte) -> (n: int, err: Error) { + return _write(fd, p); +} + +write_at :: proc(fd: Handle, p: []byte, offset: i64) -> (n: int, err: Error) { + return _write_at(fd, p, offset); +} + +write_to :: proc(fd: Handle, w: io.Writer) -> (n: i64, err: Error) { + return _write_to(fd, w); +} + +file_size :: proc(fd: Handle) -> (n: i64, err: Error) { + return _file_size(fd); +} + + +sync :: proc(fd: Handle) -> Error { + return _sync(fd); +} + +flush :: proc(fd: Handle) -> Error { + return _flush(fd); +} + +truncate :: proc(fd: Handle, size: i64) -> Maybe(Path_Error) { + return _truncate(fd, size); +} + +remove :: proc(name: string) -> Maybe(Path_Error) { + return _remove(name); +} + +rename :: proc(old_path, new_path: string) -> Maybe(Path_Error) { + return _rename(old_path, new_path); +} + + +link :: proc(old_name, new_name: string) -> Maybe(Link_Error) { + return _link(old_name, new_name); +} + +symlink :: proc(old_name, new_name: string) -> Maybe(Link_Error) { + return _symlink(old_name, new_name); +} + +read_link :: proc(name: string) -> (string, Maybe(Path_Error)) { + return _read_link(name); +} + + +chdir :: proc(fd: Handle) -> Error { + return _chdir(fd); +} + +chmod :: proc(fd: Handle, mode: File_Mode) -> Error { + return _chmod(fd, mode); +} + +chown :: proc(fd: Handle, uid, gid: int) -> Error { + return _chown(fd, uid, gid); +} + + +lchown :: proc(name: string, uid, gid: int) -> Error { + return _lchown(name, uid, gid); +} + + +chtimes :: proc(name: string, atime, mtime: time.Time) -> Maybe(Path_Error) { + return _chtimes(name, atime, mtime); +} + +exists :: proc(path: string) -> bool { + return _exists(path); +} + +is_file :: proc(path: string) -> bool { + return _is_file(path); +} + +is_dir :: proc(path: string) -> bool { + return _is_dir(path); +} + diff --git a/core/os/os2/file_stream.odin b/core/os/os2/file_stream.odin new file mode 100644 index 000000000..6877faea4 --- /dev/null +++ b/core/os/os2/file_stream.odin @@ -0,0 +1,98 @@ +package os2 + +import "core:io" + +file_to_stream :: proc(fd: Handle) -> (s: io.Stream) { + s.stream_data = rawptr(uintptr(fd)); + s.stream_vtable = _file_stream_vtable; + return; +} + +@(private) +error_to_io_error :: proc(ferr: Error) -> io.Error { + #partial switch ferr { + case .None: return .None; + case .EOF: return .EOF; + case .Unexpected_EOF: return .Unexpected_EOF; + case .Short_Write: return .Short_Write; + case .Invalid_Write: return .Invalid_Write; + case .Short_Buffer: return .Short_Buffer; + case .No_Progress: return .No_Progress; + case .Invalid_Whence: return .Invalid_Whence; + case .Invalid_Offset: return .Invalid_Offset; + case .Invalid_Unread: return .Invalid_Unread; + case .Negative_Read: return .Negative_Read; + case .Negative_Write: return .Negative_Write; + case .Negative_Count: return .Negative_Count; + case .Buffer_Full: return .Buffer_Full; + } + return .Unknown; +} + + +@(private) +_file_stream_vtable := &io.Stream_VTable{ + impl_read = proc(s: io.Stream, p: []byte) -> (n: int, err: io.Error) { + fd := Handle(uintptr(s.stream_data)); + ferr: Error; + n, ferr = read(fd, p); + err = error_to_io_error(ferr); + return; + }, + impl_read_at = proc(s: io.Stream, p: []byte, offset: i64) -> (n: int, err: io.Error) { + fd := Handle(uintptr(s.stream_data)); + ferr: Error; + n, ferr = read_at(fd, p, offset); + err = error_to_io_error(ferr); + return; + }, + impl_write_to = proc(s: io.Stream, w: io.Writer) -> (n: i64, err: io.Error) { + fd := Handle(uintptr(s.stream_data)); + ferr: Error; + n, ferr = write_to(fd, w); + err = error_to_io_error(ferr); + return; + }, + impl_write = proc(s: io.Stream, p: []byte) -> (n: int, err: io.Error) { + fd := Handle(uintptr(s.stream_data)); + ferr: Error; + n, ferr = write(fd, p); + err = error_to_io_error(ferr); + return; + }, + impl_write_at = proc(s: io.Stream, p: []byte, offset: i64) -> (n: int, err: io.Error) { + fd := Handle(uintptr(s.stream_data)); + ferr: Error; + n, ferr = write_at(fd, p, offset); + err = error_to_io_error(ferr); + return; + }, + impl_read_from = proc(s: io.Stream, r: io.Reader) -> (n: i64, err: io.Error) { + fd := Handle(uintptr(s.stream_data)); + ferr: Error; + n, ferr = read_from(fd, r); + err = error_to_io_error(ferr); + return; + }, + impl_seek = proc(s: io.Stream, offset: i64, whence: io.Seek_From) -> (i64, io.Error) { + fd := Handle(uintptr(s.stream_data)); + n, ferr := seek(fd, offset, Seek_From(whence)); + err := error_to_io_error(ferr); + return n, err; + }, + impl_size = proc(s: io.Stream) -> i64 { + fd := Handle(uintptr(s.stream_data)); + sz, _ := file_size(fd); + return sz; + }, + impl_flush = proc(s: io.Stream) -> io.Error { + fd := Handle(uintptr(s.stream_data)); + ferr := flush(fd); + return error_to_io_error(ferr); + }, + impl_close = proc(s: io.Stream) -> io.Error { + fd := Handle(uintptr(s.stream_data)); + ferr := close(fd); + return error_to_io_error(ferr); + }, +}; diff --git a/core/os/os2/file_util.odin b/core/os/os2/file_util.odin new file mode 100644 index 000000000..435eba3ab --- /dev/null +++ b/core/os/os2/file_util.odin @@ -0,0 +1,122 @@ +package os2 + +import "core:mem" +import "core:strconv" +import "core:unicode/utf8" + +write_string :: proc(fd: Handle, s: string) -> (n: int, err: Error) { + return write(fd, transmute([]byte)s); +} + +write_byte :: proc(fd: Handle, b: byte) -> (n: int, err: Error) { + return write(fd, []byte{b}); +} + +write_rune :: proc(fd: Handle, r: rune) -> (n: int, err: Error) { + if r < utf8.RUNE_SELF { + return write_byte(fd, byte(r)); + } + + b: [4]byte; + b, n = utf8.encode_rune(r); + return write(fd, b[:n]); +} + +write_encoded_rune :: proc(fd: 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(fd, '\''), &n, &err) { return; } + + switch r { + case '\a': if wrap(write_string(fd, "\\a"), &n, &err) { return; } + case '\b': if wrap(write_string(fd, "\\b"), &n, &err) { return; } + case '\e': if wrap(write_string(fd, "\\e"), &n, &err) { return; } + case '\f': if wrap(write_string(fd, "\\f"), &n, &err) { return; } + case '\n': if wrap(write_string(fd, "\\n"), &n, &err) { return; } + case '\r': if wrap(write_string(fd, "\\r"), &n, &err) { return; } + case '\t': if wrap(write_string(fd, "\\t"), &n, &err) { return; } + case '\v': if wrap(write_string(fd, "\\v"), &n, &err) { return; } + case: + if r < 32 { + if wrap(write_string(fd, "\\x"), &n, &err) { return; } + b: [2]byte; + s := strconv.append_bits(b[:], u64(r), 16, true, 64, strconv.digits, nil); + switch len(s) { + case 0: if wrap(write_string(fd, "00"), &n, &err) { return; } + case 1: if wrap(write_rune(fd, '0'), &n, &err) { return; } + case 2: if wrap(write_string(fd, s), &n, &err) { return; } + } + } else { + if wrap(write_rune(fd, r), &n, &err) { return; } + } + } + _ = wrap(write_byte(fd, '\''), &n, &err); + return; +} + + +write_ptr :: proc(fd: Handle, data: rawptr, len: int) -> (n: int, err: Error) { + s := transmute([]byte)mem.Raw_Slice{data, len}; + return write(fd, s); +} + +read_ptr :: proc(fd: Handle, data: rawptr, len: int) -> (n: int, err: Error) { + s := transmute([]byte)mem.Raw_Slice{data, len}; + return read(fd, s); +} + + + +read_entire_file :: proc(name: string, allocator := context.allocator) -> ([]byte, Error) { + f, ferr := open(name); + if ferr != nil { + return nil, ferr; + } + defer close(f); + + size: int; + if size64, err := file_size(f); err == nil { + if i64(int(size64)) != size64 { + size = int(size64); + } + } + size += 1; // for EOF + + // TODO(bill): Is this correct logic? + total: int; + data := make([]byte, size, allocator); + for { + n, err := read(f, data[total:]); + total += n; + if err != nil { + if err == .EOF { + err = nil; + } + return data[:total], err; + } + } +} + +write_entire_file :: proc(name: string, data: []byte, perm: File_Mode, truncate := true) -> Error { + flags := O_WRONLY|O_CREATE; + if truncate { + flags |= O_TRUNC; + } + f, err := open_file(name, flags, perm); + if err != nil { + return err; + } + _, err = write(f, data); + if cerr := close(f); cerr != nil && err == nil { + err = cerr; + } + return err; +} + diff --git a/core/os/os2/file_windows.odin b/core/os/os2/file_windows.odin new file mode 100644 index 000000000..97fe6b3d9 --- /dev/null +++ b/core/os/os2/file_windows.odin @@ -0,0 +1,136 @@ +//+private +package os2 + +import "core:io" +import "core:time" + +_create :: proc(name: string) -> (Handle, Error) { + return 0, .None; +} + +_open :: proc(name: string) -> (Handle, Error) { + return 0, .None; +} + +_open_file :: proc(name: string, flag: int, perm: File_Mode) -> (Handle, Error) { + return 0, .None; +} + +_close :: proc(fd: Handle) -> Error { + return .None; +} + +_name :: proc(fd: Handle, allocator := context.allocator) -> string { + return ""; +} + +_seek :: proc(fd: Handle, offset: i64, whence: Seek_From) -> (ret: i64, err: Error) { + return; +} + +_read :: proc(fd: Handle, p: []byte) -> (n: int, err: Error) { + return; +} + +_read_at :: proc(fd: Handle, p: []byte, offset: i64) -> (n: int, err: Error) { + return; +} + +_read_from :: proc(fd: Handle, r: io.Reader) -> (n: i64, err: Error) { + return; +} + +_write :: proc(fd: Handle, p: []byte) -> (n: int, err: Error) { + return; +} + +_write_at :: proc(fd: Handle, p: []byte, offset: i64) -> (n: int, err: Error) { + return; +} + +_write_to :: proc(fd: Handle, w: io.Writer) -> (n: i64, err: Error) { + return; +} + +_file_size :: proc(fd: Handle) -> (n: i64, err: Error) { + return; +} + + +_sync :: proc(fd: Handle) -> Error { + return .None; +} + +_flush :: proc(fd: Handle) -> Error { + return .None; +} + +_truncate :: proc(fd: Handle, size: i64) -> Maybe(Path_Error) { + return nil; +} + +_remove :: proc(name: string) -> Maybe(Path_Error) { + return nil; +} + +_rename :: proc(old_path, new_path: string) -> Maybe(Path_Error) { + return nil; +} + + +_link :: proc(old_name, new_name: string) -> Maybe(Link_Error) { + return nil; +} + +_symlink :: proc(old_name, new_name: string) -> Maybe(Link_Error) { + return nil; +} + +_read_link :: proc(name: string) -> (string, Maybe(Path_Error)) { + return "", nil; +} + + +_chdir :: proc(fd: Handle) -> Error { + return .None; +} + +_chmod :: proc(fd: Handle, mode: File_Mode) -> Error { + return .None; +} + +_chown :: proc(fd: Handle, uid, gid: int) -> Error { + return .None; +} + + +_lchown :: proc(name: string, uid, gid: int) -> Error { + return .None; +} + + +_chtimes :: proc(name: string, atime, mtime: time.Time) -> Maybe(Path_Error) { + return nil; +} + + +_exists :: proc(path: string) -> bool { + return false; +} + +_is_file :: proc(path: string) -> bool { + return false; +} + +_is_dir :: proc(path: string) -> bool { + return false; +} + + +_path_error_delete :: proc(perr: Maybe(Path_Error)) { + +} + +_link_error_delete :: proc(lerr: Maybe(Link_Error)) { + +} diff --git a/core/os/os2/heap.odin b/core/os/os2/heap.odin new file mode 100644 index 000000000..08605d568 --- /dev/null +++ b/core/os/os2/heap.odin @@ -0,0 +1,21 @@ +package os2 + +import "core:runtime" + +heap_allocator :: proc() -> runtime.Allocator { + return runtime.Allocator{ + procedure = heap_allocator_proc, + data = nil, + }; +} + + +heap_allocator_proc :: proc(allocator_data: rawptr, mode: runtime.Allocator_Mode, + size, alignment: int, + old_memory: rawptr, old_size: int, flags: u64 = 0, loc := #caller_location) -> rawptr { + return _heap_allocator_proc(allocator_data, mode, size, alignment, old_memory, old_size, flags, loc); +} + + +@(private) +error_allocator := heap_allocator; diff --git a/core/os/os2/heap_windows.odin b/core/os/os2/heap_windows.odin new file mode 100644 index 000000000..e0e9c906a --- /dev/null +++ b/core/os/os2/heap_windows.odin @@ -0,0 +1,107 @@ +//+private +package os2 + +import "core:runtime" +import "core:mem" +import win32 "core:sys/windows" + +heap_alloc :: proc(size: int) -> rawptr { + return win32.HeapAlloc(win32.GetProcessHeap(), win32.HEAP_ZERO_MEMORY, uint(size)); +} + +heap_resize :: proc(ptr: rawptr, new_size: int) -> rawptr { + if new_size == 0 { + heap_free(ptr); + return nil; + } + if ptr == nil { + return heap_alloc(new_size); + } + + return win32.HeapReAlloc(win32.GetProcessHeap(), win32.HEAP_ZERO_MEMORY, ptr, uint(new_size)); +} +heap_free :: proc(ptr: rawptr) { + if ptr == nil { + return; + } + win32.HeapFree(win32.GetProcessHeap(), 0, ptr); +} + +_heap_allocator_proc :: proc(allocator_data: rawptr, mode: runtime.Allocator_Mode, + size, alignment: int, + old_memory: rawptr, old_size: int, flags: u64 = 0, loc := #caller_location) -> rawptr { + // + // NOTE(tetra, 2020-01-14): The heap doesn't respect alignment. + // Instead, we overallocate by `alignment + size_of(rawptr) - 1`, and insert + // padding. We also store the original pointer returned by heap_alloc right before + // the pointer we return to the user. + // + + aligned_alloc :: proc(size, alignment: int, old_ptr: rawptr = nil) -> rawptr { + a := max(alignment, align_of(rawptr)); + space := size + a - 1; + + allocated_mem: rawptr; + if old_ptr != nil { + original_old_ptr := mem.ptr_offset((^rawptr)(old_ptr), -1)^; + allocated_mem = heap_resize(original_old_ptr, space+size_of(rawptr)); + } else { + allocated_mem = heap_alloc(space+size_of(rawptr)); + } + aligned_mem := rawptr(mem.ptr_offset((^u8)(allocated_mem), size_of(rawptr))); + + ptr := uintptr(aligned_mem); + aligned_ptr := (ptr - 1 + uintptr(a)) & -uintptr(a); + diff := int(aligned_ptr - ptr); + if (size + diff) > space { + return nil; + } + + aligned_mem = rawptr(aligned_ptr); + mem.ptr_offset((^rawptr)(aligned_mem), -1)^ = allocated_mem; + + return aligned_mem; + } + + aligned_free :: proc(p: rawptr) { + if p != nil { + heap_free(mem.ptr_offset((^rawptr)(p), -1)^); + } + } + + aligned_resize :: proc(p: rawptr, old_size: int, new_size: int, new_alignment: int) -> rawptr { + if p == nil { + return nil; + } + return aligned_alloc(new_size, new_alignment, p); + } + + switch mode { + case .Alloc: + return aligned_alloc(size, alignment); + + case .Free: + aligned_free(old_memory); + + case .Free_All: + // NOTE(tetra): Do nothing. + + case .Resize: + if old_memory == nil { + return aligned_alloc(size, alignment); + } + return aligned_resize(old_memory, old_size, size, alignment); + + case .Query_Features: + set := (^runtime.Allocator_Mode_Set)(old_memory); + if set != nil { + set^ = {.Alloc, .Free, .Resize, .Query_Features}; + } + return set; + + case .Query_Info: + return nil; + } + + return nil; +} diff --git a/core/os/os2/path.odin b/core/os/os2/path.odin new file mode 100644 index 000000000..eee2b3cee --- /dev/null +++ b/core/os/os2/path.odin @@ -0,0 +1,29 @@ +package os2 + +Path_Separator :: _Path_Separator; // OS-Specific +Path_List_Separator :: _Path_List_Separator; // OS-Specific + +is_path_separator :: proc(c: byte) -> bool { + return _is_path_separator(c); +} + +mkdir :: proc(name: string, perm: File_Mode) -> Maybe(Path_Error) { + return _mkdir(name, perm); +} + +mkdir_all :: proc(path: string, perm: File_Mode) -> Maybe(Path_Error) { + return _mkdir_all(path, perm); +} + +remove_all :: proc(path: string) -> Maybe(Path_Error) { + return _remove_all(path); +} + + + +getwd :: proc(allocator := context.allocator) -> (dir: string, err: Error) { + return _getwd(allocator); +} +setwd :: proc(dir: string) -> (err: Error) { + return _setwd(dir); +} diff --git a/core/os/os2/path_windows.odin b/core/os/os2/path_windows.odin new file mode 100644 index 000000000..5056eb638 --- /dev/null +++ b/core/os/os2/path_windows.odin @@ -0,0 +1,31 @@ +//+private +package os2 + +_Path_Separator :: '\\'; +_Path_List_Separator :: ';'; + +_is_path_separator :: proc(c: byte) -> bool { + return c == '\\' || c == '/'; +} + +_mkdir :: proc(name: string, perm: File_Mode) -> Maybe(Path_Error) { + return nil; +} + +_mkdir_all :: proc(path: string, perm: File_Mode) -> Maybe(Path_Error) { + // TODO(bill): _mkdir_all for windows + return nil; +} + +_remove_all :: proc(path: string) -> Maybe(Path_Error) { + // TODO(bill): _remove_all for windows + return nil; +} + +_getwd :: proc(allocator := context.allocator) -> (dir: string, err: Error) { + return "", nil; +} + +_setwd :: proc(dir: string) -> (err: Error) { + return nil; +} diff --git a/core/os/os2/pipe.odin b/core/os/os2/pipe.odin new file mode 100644 index 000000000..8bb46b303 --- /dev/null +++ b/core/os/os2/pipe.odin @@ -0,0 +1,5 @@ +package os2 + +pipe :: proc() -> (r, w: Handle, err: Error) { + return _pipe(); +} diff --git a/core/os/os2/pipe_windows.odin b/core/os/os2/pipe_windows.odin new file mode 100644 index 000000000..68adb6c3b --- /dev/null +++ b/core/os/os2/pipe_windows.odin @@ -0,0 +1,13 @@ +//+private +package os2 + +import win32 "core:sys/windows" + +_pipe :: proc() -> (r, w: Handle, err: Error) { + p: [2]win32.HANDLE; + if !win32.CreatePipe(&p[0], &p[1], nil, 0) { + return 0, 0, error_from_platform_error(i32(win32.GetLastError())); + } + return Handle(p[0]), Handle(p[1]), nil; +} + diff --git a/core/os/os2/process.odin b/core/os/os2/process.odin new file mode 100644 index 000000000..f0060b54f --- /dev/null +++ b/core/os/os2/process.odin @@ -0,0 +1,101 @@ +package os2 + +import sync "core:sync/sync2" +import "core:time" + +args: []string; + +exit :: proc "contextless" (code: int) -> ! { + // +} + +get_uid :: proc() -> int { + return -1; +} + +get_euid :: proc() -> int { + return -1; +} + +get_gid :: proc() -> int { + return -1; +} + +get_egid :: proc() -> int { + return -1; +} + +get_pid :: proc() -> int { + return -1; +} + +get_ppid :: proc() -> int { + return -1; +} + + +Process :: struct { + pid: int, + handle: uintptr, + is_done: b32, + signal_mutex: sync.RW_Mutex, +} + + +Process_Attributes :: struct { + dir: string, + env: []string, + files: []Handle, + sys: ^Process_Attributes_OS_Specific, +} + +Process_Attributes_OS_Specific :: struct{}; + +Process_Error :: enum { + None, +} + +Process_State :: struct { + pid: int, + exit_code: int, + exited: bool, + success: bool, + system_time: time.Duration, + user_time: time.Duration, + sys: rawptr, +} + +Signal :: #type proc(); + +Kill: Signal = nil; +Interrupt: Signal = nil; + + +find_process :: proc(pid: int) -> (^Process, Process_Error) { + return nil, .None; +} + + +process_start :: proc(name: string, argv: []string, attr: ^Process_Attributes) -> (^Process, Process_Error) { + return nil, .None; +} + +process_release :: proc(p: ^Process) -> Process_Error { + return .None; +} + +process_kill :: proc(p: ^Process) -> Process_Error { + return .None; +} + +process_signal :: proc(p: ^Process, sig: Signal) -> Process_Error { + return .None; +} + +process_wait :: proc(p: ^Process) -> (Process_State, Process_Error) { + return {}, .None; +} + + + + diff --git a/core/os/os2/stat.odin b/core/os/os2/stat.odin new file mode 100644 index 000000000..791948c8d --- /dev/null +++ b/core/os/os2/stat.odin @@ -0,0 +1,42 @@ +package os2 + +import "core:time" + +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, +} + +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); +} + +fstat :: proc(fd: Handle, allocator := context.allocator) -> (File_Info, Maybe(Path_Error)) { + return _fstat(fd, allocator); +} + +stat :: proc(name: string, allocator := context.allocator) -> (File_Info, Maybe(Path_Error)) { + return _stat(name, allocator); +} + +lstat :: proc(name: string, allocator := context.allocator) -> (File_Info, Maybe(Path_Error)) { + return _lstat(name, allocator); +} + + +same_file :: proc(fi1, fi2: File_Info) -> bool { + return _same_file(fi1, fi2); +} diff --git a/core/os/os2/stat_windows.odin b/core/os/os2/stat_windows.odin new file mode 100644 index 000000000..ed739b894 --- /dev/null +++ b/core/os/os2/stat_windows.odin @@ -0,0 +1,373 @@ +//+private +package os2 + +import "core:time" +import win32 "core:sys/windows" + +_fstat :: proc(fd: Handle, allocator := context.allocator) -> (File_Info, Maybe(Path_Error)) { + if fd == 0 { + return {}, Path_Error{err = .Invalid_Argument}; + } + context.allocator = allocator; + + path, err := _cleanpath_from_handle(fd); + if err != nil { + return {}, err; + } + + h := win32.HANDLE(fd); + switch win32.GetFileType(h) { + case win32.FILE_TYPE_PIPE, win32.FILE_TYPE_CHAR: + fi: File_Info; + fi.fullpath = path; + fi.name = basename(path); + fi.mode |= file_type_mode(h); + return fi, nil; + } + + return _file_info_from_get_file_information_by_handle(path, h); +} +_stat :: proc(name: string, allocator := context.allocator) -> (File_Info, Maybe(Path_Error)) { + return internal_stat(name, win32.FILE_FLAG_BACKUP_SEMANTICS); +} +_lstat :: proc(name: string, allocator := context.allocator) -> (File_Info, Maybe(Path_Error)) { + return internal_stat(name, win32.FILE_FLAG_BACKUP_SEMANTICS|win32.FILE_FLAG_OPEN_REPARSE_POINT); +} +_same_file :: proc(fi1, fi2: File_Info) -> bool { + return fi1.fullpath == fi2.fullpath; +} + + + +_stat_errno :: proc(errno: win32.DWORD) -> Path_Error { + return Path_Error{err = error_from_platform_error(i32(errno))}; +} + + +full_path_from_name :: proc(name: string, allocator := context.allocator) -> (path: string, err: Maybe(Path_Error)) { + name := name; + if name == "" { + name = "."; + } + p := win32.utf8_to_utf16(name, context.temp_allocator); + buf := make([dynamic]u16, 100, allocator); + for { + n := win32.GetFullPathNameW(raw_data(p), u32(len(buf)), raw_data(buf), nil); + if n == 0 { + delete(buf); + return "", _stat_errno(win32.GetLastError()); + } + if n <= u32(len(buf)) { + return win32.utf16_to_utf8(buf[:n]), nil; + } + resize(&buf, len(buf)*2); + } + + return; +} + + +internal_stat :: proc(name: string, create_file_attributes: u32, allocator := context.allocator) -> (fi: File_Info, e: Maybe(Path_Error)) { + if len(name) == 0 { + return {}, Path_Error{err = .Not_Exist}; + } + + context.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 = Path_Error{err = error_from_platform_error(i32(win32.GetLastError()))}; + 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 = Path_Error{err = error_from_platform_error(i32(win32.GetLastError()))}; + return; + } + defer win32.CloseHandle(h); + return _file_info_from_get_file_information_by_handle(name, h); +} + + +_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 { + if buf[0] == '\\' && + buf[1] == '\\' && + buf[2] == '?' && + buf[3] == '\\' { + buf = buf[4:]; + } + } + return buf; +} + + +_cleanpath_from_handle :: proc(fd: Handle) -> (string, Maybe(Path_Error)) { + if fd == 0 { + return "", Path_Error{err = .Invalid_Argument}; + } + h := win32.HANDLE(fd); + + MAX_PATH := win32.DWORD(260) + 1; + buf: []u16; + for { + buf = make([]u16, MAX_PATH, context.temp_allocator); + err := win32.GetFinalPathNameByHandleW(h, raw_data(buf), MAX_PATH, 0); + switch err { + case win32.ERROR_PATH_NOT_FOUND, win32.ERROR_INVALID_PARAMETER: + return "", _stat_errno(err); + case win32.ERROR_NOT_ENOUGH_MEMORY: + MAX_PATH = MAX_PATH*2 + 1; + continue; + } + break; + } + return _cleanpath_from_buf(buf), nil; +} + +_cleanpath_from_handle_u16 :: proc(fd: Handle) -> ([]u16, Maybe(Path_Error)) { + if fd == 0 { + return nil, Path_Error{err = .Invalid_Argument}; + } + h := win32.HANDLE(fd); + + MAX_PATH := win32.DWORD(260) + 1; + buf: []u16; + for { + buf = make([]u16, MAX_PATH, context.temp_allocator); + err := win32.GetFinalPathNameByHandleW(h, raw_data(buf), MAX_PATH, 0); + switch err { + case win32.ERROR_PATH_NOT_FOUND, win32.ERROR_INVALID_PARAMETER: + return nil, _stat_errno(err); + case win32.ERROR_NOT_ENOUGH_MEMORY: + MAX_PATH = MAX_PATH*2 + 1; + continue; + } + break; + } + return _cleanpath_strip_prefix(buf), nil; +} + +_cleanpath_from_buf :: proc(buf: []u16) -> string { + buf := buf; + buf = _cleanpath_strip_prefix(buf); + return win32.utf16_to_utf8(buf, context.allocator); +} + + +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; +} + + +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; +} + + + +_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; +} + + +_file_info_from_win32_file_attribute_data :: proc(d: ^win32.WIN32_FILE_ATTRIBUTE_DATA, name: string) -> (fi: File_Info, e: Maybe(Path_Error)) { + 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; + + fi.creation_time = time.unix(0, win32.FILETIME_as_unix_nanoseconds(d.ftCreationTime)); + fi.modification_time = time.unix(0, win32.FILETIME_as_unix_nanoseconds(d.ftLastWriteTime)); + fi.access_time = time.unix(0, win32.FILETIME_as_unix_nanoseconds(d.ftLastAccessTime)); + + fi.fullpath, e = full_path_from_name(name); + fi.name = basename(fi.fullpath); + + return; +} + + +_file_info_from_win32_find_data :: proc(d: ^win32.WIN32_FIND_DATAW, name: string) -> (fi: File_Info, e: Maybe(Path_Error)) { + 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; + + fi.creation_time = time.unix(0, win32.FILETIME_as_unix_nanoseconds(d.ftCreationTime)); + fi.modification_time = time.unix(0, win32.FILETIME_as_unix_nanoseconds(d.ftLastWriteTime)); + fi.access_time = time.unix(0, win32.FILETIME_as_unix_nanoseconds(d.ftLastAccessTime)); + + fi.fullpath, e = full_path_from_name(name); + fi.name = basename(fi.fullpath); + + return; +} + + +_file_info_from_get_file_information_by_handle :: proc(path: string, h: win32.HANDLE) -> (File_Info, Maybe(Path_Error)) { + d: win32.BY_HANDLE_FILE_INFORMATION; + if !win32.GetFileInformationByHandle(h, &d) { + return {}, _stat_errno(win32.GetLastError()); + + } + + ti: win32.FILE_ATTRIBUTE_TAG_INFO; + if !win32.GetFileInformationByHandleEx(h, .FileAttributeTagInfo, &ti, size_of(ti)) { + err := win32.GetLastError(); + if err != win32.ERROR_INVALID_PARAMETER { + return {}, _stat_errno(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; + + fi.creation_time = time.unix(0, win32.FILETIME_as_unix_nanoseconds(d.ftCreationTime)); + fi.modification_time = time.unix(0, win32.FILETIME_as_unix_nanoseconds(d.ftLastWriteTime)); + fi.access_time = time.unix(0, win32.FILETIME_as_unix_nanoseconds(d.ftLastAccessTime)); + + return fi, nil; +} + +_is_abs :: proc(path: string) -> bool { + if len(path) > 0 && path[0] == '/' { + return true; + } + if len(path) > 2 { + switch path[0] { + case 'A'..'Z', 'a'..'z': + return path[1] == ':' && is_path_separator(path[2]); + } + } + return false; +} + +_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]); +} diff --git a/core/os/os2/temp_file.odin b/core/os/os2/temp_file.odin new file mode 100644 index 000000000..4969a07d9 --- /dev/null +++ b/core/os/os2/temp_file.odin @@ -0,0 +1,14 @@ +package os2 + + +create_temp :: proc(dir, pattern: string) -> (Handle, Error) { + return _create_temp(dir, pattern); +} + +mkdir_temp :: proc(dir, pattern: string, allocator := context.allocator) -> (string, Error) { + return _mkdir_temp(dir, pattern); +} + +temp_dir :: proc(allocator := context.allocator) -> string { + return _temp_dir(allocator); +} diff --git a/core/os/os2/temp_file_windows.odin b/core/os/os2/temp_file_windows.odin new file mode 100644 index 000000000..19dca1b04 --- /dev/null +++ b/core/os/os2/temp_file_windows.odin @@ -0,0 +1,29 @@ +//+private +package os2 + +import win32 "core:sys/windows" + +_create_temp :: proc(dir, pattern: string) -> (Handle, Error) { + return 0, .None; +} + +_mkdir_temp :: proc(dir, pattern: string, allocator := context.allocator) -> (string, Error) { + return "", .None; +} + +_temp_dir :: proc(allocator := context.allocator) -> string { + b := make([dynamic]u16, u32(win32.MAX_PATH), context.temp_allocator); + for { + n := win32.GetTempPathW(u32(len(b)), raw_data(b)); + if n > u32(len(b)) { + resize(&b, int(n)); + continue; + } + if n == 3 && b[1] == ':' && b[2] == '\\' { + + } else if n > 0 && b[n-1] == '\\' { + n -= 1; + } + return win32.utf16_to_utf8(b[:n], allocator); + } +} diff --git a/core/os/os2/user.odin b/core/os/os2/user.odin new file mode 100644 index 000000000..b23597387 --- /dev/null +++ b/core/os/os2/user.odin @@ -0,0 +1,68 @@ +package os2 + +import "core:strings" + +user_cache_dir :: proc(allocator := context.allocator) -> (dir: string, is_defined: bool) { + switch ODIN_OS { + case "windows": + dir = get_env("LocalAppData"); + if dir != "" { + dir = strings.clone(dir, allocator); + } + case "darwin": + dir = get_env("HOME"); + if dir != "" { + dir = strings.concatenate({dir, "/Library/Caches"}, allocator); + } + case: // All other UNIX systems + dir = get_env("XDG_CACHE_HOME"); + if dir == "" { + dir = get_env("HOME"); + if dir == "" { + return; + } + dir = strings.concatenate({dir, "/.cache"}, allocator); + } + } + is_defined = dir != ""; + return; +} + +user_config_dir :: proc(allocator := context.allocator) -> (dir: string, is_defined: bool) { + switch ODIN_OS { + case "windows": + dir = get_env("AppData"); + if dir != "" { + dir = strings.clone(dir, allocator); + } + case "darwin": + dir = get_env("HOME"); + if dir != "" { + dir = strings.concatenate({dir, "/Library/Application Support"}, allocator); + } + case: // All other UNIX systems + dir = get_env("XDG_CACHE_HOME"); + if dir == "" { + dir = get_env("HOME"); + if dir == "" { + return; + } + dir = strings.concatenate({dir, "/.config"}, allocator); + } + } + is_defined = dir != ""; + return; +} + +user_home_dir :: proc() -> (dir: string, is_defined: bool) { + env := "HOME"; + switch ODIN_OS { + case "windows": + env = "USERPROFILE"; + } + if v := get_env(env); v != "" { + return v, true; + } + return "", false; +} + |