mirror of
https://github.com/odin-lang/Odin.git
synced 2026-01-10 06:53:03 +00:00
Begin mocking os2 for windows out more
This commit is contained in:
@@ -11,6 +11,8 @@ General_Error :: enum u32 {
|
||||
Closed,
|
||||
|
||||
Timeout,
|
||||
|
||||
Invalid_File,
|
||||
}
|
||||
|
||||
Platform_Error :: struct {
|
||||
@@ -24,36 +26,6 @@ Error :: union {
|
||||
}
|
||||
#assert(size_of(Error) == size_of(u64))
|
||||
|
||||
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) {
|
||||
|
||||
@@ -21,14 +21,27 @@ 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)
|
||||
File_Flags :: distinct bit_set[File_Flag; uint]
|
||||
File_Flag :: enum {
|
||||
Read,
|
||||
Write,
|
||||
Append,
|
||||
Create,
|
||||
Excl,
|
||||
Sync,
|
||||
Trunc,
|
||||
Sparse,
|
||||
}
|
||||
|
||||
O_RDONLY :: File_Flags{.Read}
|
||||
O_WRONLY :: File_Flags{.Write}
|
||||
O_RDWR :: File_Flags{.Read, .Write}
|
||||
O_APPEND :: File_Flags{.Append}
|
||||
O_CREATE :: File_Flags{.Create}
|
||||
O_EXCL :: File_Flags{.Excl}
|
||||
O_SYNC :: File_Flags{.Sync}
|
||||
O_TRUNC :: File_Flags{.Trunc}
|
||||
O_SPARSE :: File_Flags{.Sparse}
|
||||
|
||||
|
||||
|
||||
@@ -38,27 +51,27 @@ stderr: ^File = nil // OS-Specific
|
||||
|
||||
|
||||
create :: proc(name: string) -> (^File, Error) {
|
||||
return _create(name)
|
||||
return open(name, {.Read, .Write, .Create}, File_Mode(0o777))
|
||||
}
|
||||
|
||||
open :: proc(name: string) -> (^File, Error) {
|
||||
return _open(name)
|
||||
}
|
||||
|
||||
open_file :: proc(name: string, flag: int, perm: File_Mode) -> (^File, Error) {
|
||||
return _open_file(name, flag, perm)
|
||||
open :: proc(name: string, flags := File_Flags{.Read}, perm := File_Mode(0o777)) -> (^File, Error) {
|
||||
return _open(name, flags, perm)
|
||||
}
|
||||
|
||||
new_file :: proc(handle: uintptr, name: string) -> ^File {
|
||||
return _new_file(handle, name)
|
||||
}
|
||||
|
||||
fd :: proc(f: ^File) -> uintptr {
|
||||
return _fd(f)
|
||||
}
|
||||
|
||||
|
||||
close :: proc(f: ^File) -> Error {
|
||||
return _close(f)
|
||||
}
|
||||
|
||||
name :: proc(f: ^File, allocator := context.allocator) -> string {
|
||||
name :: proc(f: ^File) -> string {
|
||||
return _name(f)
|
||||
}
|
||||
|
||||
@@ -103,28 +116,28 @@ flush :: proc(f: ^File) -> Error {
|
||||
return _flush(f)
|
||||
}
|
||||
|
||||
truncate :: proc(f: ^File, size: i64) -> Maybe(Path_Error) {
|
||||
truncate :: proc(f: ^File, size: i64) -> Error {
|
||||
return _truncate(f, size)
|
||||
}
|
||||
|
||||
remove :: proc(name: string) -> Maybe(Path_Error) {
|
||||
remove :: proc(name: string) -> Error {
|
||||
return _remove(name)
|
||||
}
|
||||
|
||||
rename :: proc(old_path, new_path: string) -> Maybe(Path_Error) {
|
||||
rename :: proc(old_path, new_path: string) -> Error {
|
||||
return _rename(old_path, new_path)
|
||||
}
|
||||
|
||||
|
||||
link :: proc(old_name, new_name: string) -> Maybe(Link_Error) {
|
||||
link :: proc(old_name, new_name: string) -> Error {
|
||||
return _link(old_name, new_name)
|
||||
}
|
||||
|
||||
symlink :: proc(old_name, new_name: string) -> Maybe(Link_Error) {
|
||||
symlink :: proc(old_name, new_name: string) -> Error {
|
||||
return _symlink(old_name, new_name)
|
||||
}
|
||||
|
||||
read_link :: proc(name: string) -> (string, Maybe(Path_Error)) {
|
||||
read_link :: proc(name: string) -> (string, Error) {
|
||||
return _read_link(name)
|
||||
}
|
||||
|
||||
@@ -147,7 +160,7 @@ lchown :: proc(name: string, uid, gid: int) -> Error {
|
||||
}
|
||||
|
||||
|
||||
chtimes :: proc(name: string, atime, mtime: time.Time) -> Maybe(Path_Error) {
|
||||
chtimes :: proc(name: string, atime, mtime: time.Time) -> Error {
|
||||
return _chtimes(name, atime, mtime)
|
||||
}
|
||||
|
||||
|
||||
@@ -109,7 +109,7 @@ write_entire_file :: proc(name: string, data: []byte, perm: File_Mode, truncate
|
||||
if truncate {
|
||||
flags |= O_TRUNC
|
||||
}
|
||||
f, err := open_file(name, flags, perm)
|
||||
f, err := open(name, flags, perm)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -3,38 +3,119 @@ package os2
|
||||
|
||||
import "core:io"
|
||||
import "core:time"
|
||||
import "core:runtime"
|
||||
import "core:strings"
|
||||
import win32 "core:sys/windows"
|
||||
|
||||
INVALID_HANDLE :: ~uintptr(0)
|
||||
|
||||
_file_allocator :: proc() -> runtime.Allocator {
|
||||
return heap_allocator()
|
||||
}
|
||||
|
||||
_File_Kind :: enum u8 {
|
||||
File,
|
||||
Console,
|
||||
Pipe,
|
||||
}
|
||||
|
||||
_File :: struct {
|
||||
fd: rawptr,
|
||||
name: string,
|
||||
wname: win32.wstring,
|
||||
kind: _File_Kind,
|
||||
}
|
||||
|
||||
_create :: proc(name: string) -> (^File, Error) {
|
||||
return nil, nil
|
||||
_get_platform_error :: proc() -> Error {
|
||||
err := i32(win32.GetLastError())
|
||||
if err != 0 {
|
||||
return Platform_Error{err}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
_open :: proc(name: string) -> (^File, Error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
_open_file :: proc(name: string, flag: int, perm: File_Mode) -> (^File, Error) {
|
||||
_open :: proc(name: string, flags: File_Flags, perm: File_Mode) -> (^File, Error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
_new_file :: proc(handle: uintptr, name: string) -> ^File {
|
||||
if handle == INVALID_HANDLE {
|
||||
return nil
|
||||
}
|
||||
context.allocator = _file_allocator()
|
||||
f := new(File)
|
||||
f.impl.fd = rawptr(fd)
|
||||
f.impl.name = strings.clone(name, context.allocator)
|
||||
f.impl.wname = win32.utf8_to_wstring(name, context.allocator)
|
||||
|
||||
kind := _File_Kind.File
|
||||
if m: u32; win32.GetConsoleMode(win32.HANDLE(fd), &m) {
|
||||
kind = .Console
|
||||
}
|
||||
if win32.GetFileType(win32.HANDLE(fd)) == win32.FILE_TYPE_PIPE {
|
||||
kind = .Pipe
|
||||
}
|
||||
f.impl.kind = kind
|
||||
|
||||
return f
|
||||
}
|
||||
|
||||
_fd :: proc(f: ^File) -> uintptr {
|
||||
if f == nil {
|
||||
return INVALID_HANDLE
|
||||
}
|
||||
return uintptr(f.impl.fd)
|
||||
}
|
||||
|
||||
_destroy :: proc(f: ^File) -> Error {
|
||||
if f == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
context.allocator = _file_allocator()
|
||||
free(f.impl.wname)
|
||||
delete(f.impl.name)
|
||||
free(f)
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
_close :: proc(f: ^File) -> Error {
|
||||
return nil
|
||||
if f == nil {
|
||||
return nil
|
||||
}
|
||||
if !win32.CloseHandle(win32.HANDLE(f.impl.fd)) {
|
||||
return .Closed
|
||||
}
|
||||
return _destroy(f)
|
||||
}
|
||||
|
||||
_name :: proc(f: ^File, allocator := context.allocator) -> string {
|
||||
return ""
|
||||
_name :: proc(f: ^File) -> string {
|
||||
return f.impl.name if f != nil else ""
|
||||
}
|
||||
|
||||
_seek :: proc(f: ^File, offset: i64, whence: Seek_From) -> (ret: i64, err: Error) {
|
||||
return
|
||||
if f == nil {
|
||||
return
|
||||
}
|
||||
w: u32
|
||||
switch whence {
|
||||
case .Start: w = win32.FILE_BEGIN
|
||||
case .Current: w = win32.FILE_CURRENT
|
||||
case .End: w = win32.FILE_END
|
||||
}
|
||||
hi := i32(offset>>32)
|
||||
lo := i32(offset)
|
||||
ft := win32.GetFileType(win32.HANDLE(fd))
|
||||
if ft == win32.FILE_TYPE_PIPE {
|
||||
return 0, .Invalid_File
|
||||
}
|
||||
|
||||
dw_ptr := win32.SetFilePointer(win32.HANDLE(fd), lo, &hi, w)
|
||||
if dw_ptr == win32.INVALID_SET_FILE_POINTER {
|
||||
return 0, _get_platform_error()
|
||||
}
|
||||
return i64(hi)<<32 + i64(dw_ptr), nil
|
||||
}
|
||||
|
||||
_read :: proc(f: ^File, p: []byte) -> (n: int, err: Error) {
|
||||
@@ -62,6 +143,14 @@ _write_to :: proc(f: ^File, w: io.Writer) -> (n: i64, err: Error) {
|
||||
}
|
||||
|
||||
_file_size :: proc(f: ^File) -> (n: i64, err: Error) {
|
||||
if f == nil {
|
||||
return
|
||||
}
|
||||
length: win32.LARGE_INTEGER
|
||||
if !win32.GetFileSizeEx(win32.HANDLE(fd), &length) {
|
||||
err = _get_platform_error()
|
||||
}
|
||||
n = i64(length)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -74,34 +163,93 @@ _flush :: proc(f: ^File) -> Error {
|
||||
return nil
|
||||
}
|
||||
|
||||
_truncate :: proc(f: ^File, size: i64) -> Maybe(Path_Error) {
|
||||
_truncate :: proc(f: ^File, size: i64) -> Error {
|
||||
if f == nil {
|
||||
return nil
|
||||
}
|
||||
curr_off := seek(f, 0, .Current) or_return
|
||||
defer seek(f, curr_off, .Start)
|
||||
seek(f, size, .Start) or_return
|
||||
if !win32.SetEndOfFile(win32.HANDLE(fd)) {
|
||||
return _get_platform_error()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
_remove :: proc(name: string) -> Maybe(Path_Error) {
|
||||
return nil
|
||||
_remove :: proc(name: string) -> Error {
|
||||
p := _fix_long_path(name)
|
||||
err, err1: Error
|
||||
if !win32.DeleteFileW(p) {
|
||||
err = _get_platform_error()
|
||||
}
|
||||
if err == nil {
|
||||
return nil
|
||||
}
|
||||
if !win32.RemoveDirectoryW(p) {
|
||||
err1 = _get_platform_error()
|
||||
}
|
||||
if err1 == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
if err != err1 {
|
||||
a := win32.GetFileAttributesW(p)
|
||||
if a == ~u32(0) {
|
||||
err = _get_platform_error()
|
||||
} 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 = nil
|
||||
if !win32.DeleteFileW(p) {
|
||||
err = _get_platform_error()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
_rename :: proc(old_path, new_path: string) -> Maybe(Path_Error) {
|
||||
return nil
|
||||
_rename :: proc(old_path, new_path: string) -> Error {
|
||||
from := _fix_long_path(old_path)
|
||||
to := _fix_long_path(new_path)
|
||||
if win32.MoveFileExW(from, to, win32.MOVEFILE_REPLACE_EXISTING) {
|
||||
return nil
|
||||
}
|
||||
return _get_platform_error()
|
||||
|
||||
}
|
||||
|
||||
|
||||
_link :: proc(old_name, new_name: string) -> Maybe(Link_Error) {
|
||||
_link :: proc(old_name, new_name: string) -> Error {
|
||||
o := _fix_long_path(old_name)
|
||||
n := _fix_long_path(new_name)
|
||||
if win32.CreateHardLinkW(n, o, nil) {
|
||||
return nil
|
||||
}
|
||||
return _get_platform_error()
|
||||
}
|
||||
|
||||
_symlink :: proc(old_name, new_name: string) -> Error {
|
||||
return nil
|
||||
}
|
||||
|
||||
_symlink :: proc(old_name, new_name: string) -> Maybe(Link_Error) {
|
||||
return nil
|
||||
}
|
||||
|
||||
_read_link :: proc(name: string) -> (string, Maybe(Path_Error)) {
|
||||
_read_link :: proc(name: string) -> (string, Error) {
|
||||
return "", nil
|
||||
}
|
||||
|
||||
|
||||
_chdir :: proc(f: ^File) -> Error {
|
||||
return nil
|
||||
if f == nil {
|
||||
return nil
|
||||
}
|
||||
if win32.SetCurrentDirectoryW(f.impl.wname) {
|
||||
return nil
|
||||
}
|
||||
return _get_platform_error()
|
||||
}
|
||||
|
||||
_chmod :: proc(f: ^File, mode: File_Mode) -> Error {
|
||||
@@ -118,28 +266,31 @@ _lchown :: proc(name: string, uid, gid: int) -> Error {
|
||||
}
|
||||
|
||||
|
||||
_chtimes :: proc(name: string, atime, mtime: time.Time) -> Maybe(Path_Error) {
|
||||
_chtimes :: proc(name: string, atime, mtime: time.Time) -> Error {
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
_exists :: proc(path: string) -> bool {
|
||||
return false
|
||||
wpath := _fix_long_path(path)
|
||||
attribs := win32.GetFileAttributesW(wpath)
|
||||
return i32(attribs) != win32.INVALID_FILE_ATTRIBUTES
|
||||
}
|
||||
|
||||
_is_file :: proc(path: string) -> bool {
|
||||
wpath := _fix_long_path(path)
|
||||
attribs := win32.GetFileAttributesW(wpath)
|
||||
if i32(attribs) != win32.INVALID_FILE_ATTRIBUTES {
|
||||
return attribs & win32.FILE_ATTRIBUTE_DIRECTORY == 0
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
_is_dir :: proc(path: string) -> bool {
|
||||
wpath := _fix_long_path(path)
|
||||
attribs := win32.GetFileAttributesW(wpath)
|
||||
if i32(attribs) != win32.INVALID_FILE_ATTRIBUTES {
|
||||
return attribs & win32.FILE_ATTRIBUTE_DIRECTORY != 0
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
|
||||
_path_error_delete :: proc(perr: Maybe(Path_Error)) {
|
||||
|
||||
}
|
||||
|
||||
_link_error_delete :: proc(lerr: Maybe(Link_Error)) {
|
||||
|
||||
}
|
||||
|
||||
@@ -7,15 +7,15 @@ is_path_separator :: proc(c: byte) -> bool {
|
||||
return _is_path_separator(c)
|
||||
}
|
||||
|
||||
mkdir :: proc(name: string, perm: File_Mode) -> Maybe(Path_Error) {
|
||||
mkdir :: proc(name: string, perm: File_Mode) -> Error {
|
||||
return _mkdir(name, perm)
|
||||
}
|
||||
|
||||
mkdir_all :: proc(path: string, perm: File_Mode) -> Maybe(Path_Error) {
|
||||
mkdir_all :: proc(path: string, perm: File_Mode) -> Error {
|
||||
return _mkdir_all(path, perm)
|
||||
}
|
||||
|
||||
remove_all :: proc(path: string) -> Maybe(Path_Error) {
|
||||
remove_all :: proc(path: string) -> Error {
|
||||
return _remove_all(path)
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
//+private
|
||||
package os2
|
||||
|
||||
import win32 "core:sys/windows"
|
||||
|
||||
_Path_Separator :: '\\'
|
||||
_Path_List_Separator :: ';'
|
||||
|
||||
@@ -8,16 +10,16 @@ _is_path_separator :: proc(c: byte) -> bool {
|
||||
return c == '\\' || c == '/'
|
||||
}
|
||||
|
||||
_mkdir :: proc(name: string, perm: File_Mode) -> Maybe(Path_Error) {
|
||||
_mkdir :: proc(name: string, perm: File_Mode) -> Error {
|
||||
return nil
|
||||
}
|
||||
|
||||
_mkdir_all :: proc(path: string, perm: File_Mode) -> Maybe(Path_Error) {
|
||||
_mkdir_all :: proc(path: string, perm: File_Mode) -> Error {
|
||||
// TODO(bill): _mkdir_all for windows
|
||||
return nil
|
||||
}
|
||||
|
||||
_remove_all :: proc(path: string) -> Maybe(Path_Error) {
|
||||
_remove_all :: proc(path: string) -> Error {
|
||||
// TODO(bill): _remove_all for windows
|
||||
return nil
|
||||
}
|
||||
@@ -29,3 +31,81 @@ _getwd :: proc(allocator := context.allocator) -> (dir: string, err: Error) {
|
||||
_setwd :: proc(dir: string) -> (err: Error) {
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
can_use_long_paths: bool
|
||||
|
||||
@(init)
|
||||
init_long_path_support :: proc() {
|
||||
// TODO(bill): init_long_path_support
|
||||
// ADD THIS SHIT
|
||||
// registry_path := win32.L(`Computer\HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\FileSystem\LongPathsEnabled`)
|
||||
can_use_long_paths = false
|
||||
}
|
||||
|
||||
|
||||
_fix_long_path_slice :: proc(path: string) -> []u16 {
|
||||
return win32.utf8_to_utf16(_fix_long_path_internal(path))
|
||||
}
|
||||
|
||||
_fix_long_path :: proc(path: string) -> win32.wstring {
|
||||
return win32.utf8_to_wstring(_fix_long_path_internal(path))
|
||||
}
|
||||
|
||||
|
||||
_fix_long_path_internal :: proc(path: string) -> string {
|
||||
if can_use_long_paths {
|
||||
return path
|
||||
}
|
||||
|
||||
// When using win32 to create a directory, the path
|
||||
// cannot be too long that you cannot append an 8.3
|
||||
// file name, because MAX_PATH is 260, 260-12 = 248
|
||||
if len(path) < 248 {
|
||||
return path
|
||||
}
|
||||
|
||||
// UNC paths do not need to be modified
|
||||
if len(path) >= 2 && path[:2] == `\\` {
|
||||
return path
|
||||
}
|
||||
|
||||
if !_is_abs(path) { // relative path
|
||||
return path
|
||||
}
|
||||
|
||||
PREFIX :: `\\?`
|
||||
path_buf := make([]byte, len(PREFIX)+len(path)+1, 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])):
|
||||
// Skip \..\ paths
|
||||
return path
|
||||
case:
|
||||
path_buf[w] = '\\'
|
||||
w += 1
|
||||
for r < n && !is_path_separator(path[r]) {
|
||||
path_buf[w] = path[r]
|
||||
r += 1
|
||||
w += 1
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Root directories require a trailing \
|
||||
if w == len(`\\?\c:`) {
|
||||
path_buf[w] = '\\'
|
||||
w += 1
|
||||
}
|
||||
|
||||
return string(path_buf[:w])
|
||||
|
||||
}
|
||||
|
||||
@@ -24,15 +24,15 @@ file_info_delete :: proc(fi: File_Info, allocator := context.allocator) {
|
||||
delete(fi.fullpath, allocator)
|
||||
}
|
||||
|
||||
fstat :: proc(f: ^File, allocator := context.allocator) -> (File_Info, Maybe(Path_Error)) {
|
||||
fstat :: proc(f: ^File, allocator := context.allocator) -> (File_Info, Error) {
|
||||
return _fstat(f, allocator)
|
||||
}
|
||||
|
||||
stat :: proc(name: string, allocator := context.allocator) -> (File_Info, Maybe(Path_Error)) {
|
||||
stat :: proc(name: string, allocator := context.allocator) -> (File_Info, Error) {
|
||||
return _stat(name, allocator)
|
||||
}
|
||||
|
||||
lstat :: proc(name: string, allocator := context.allocator) -> (File_Info, Maybe(Path_Error)) {
|
||||
lstat :: proc(name: string, allocator := context.allocator) -> (File_Info, Error) {
|
||||
return _lstat(name, allocator)
|
||||
}
|
||||
|
||||
|
||||
@@ -2,11 +2,12 @@
|
||||
package os2
|
||||
|
||||
import "core:time"
|
||||
import "core:strings"
|
||||
import win32 "core:sys/windows"
|
||||
|
||||
_fstat :: proc(f: ^File, allocator := context.allocator) -> (File_Info, Maybe(Path_Error)) {
|
||||
_fstat :: proc(f: ^File, allocator := context.allocator) -> (File_Info, Error) {
|
||||
if f == nil || f.impl.fd == nil {
|
||||
return {}, Path_Error{err = .Invalid_Argument}
|
||||
return {}, .Invalid_Argument
|
||||
}
|
||||
context.allocator = allocator
|
||||
|
||||
@@ -27,10 +28,10 @@ _fstat :: proc(f: ^File, allocator := context.allocator) -> (File_Info, Maybe(Pa
|
||||
|
||||
return _file_info_from_get_file_information_by_handle(path, h)
|
||||
}
|
||||
_stat :: proc(name: string, allocator := context.allocator) -> (File_Info, Maybe(Path_Error)) {
|
||||
_stat :: proc(name: string, allocator := context.allocator) -> (File_Info, Error) {
|
||||
return internal_stat(name, win32.FILE_FLAG_BACKUP_SEMANTICS)
|
||||
}
|
||||
_lstat :: proc(name: string, allocator := context.allocator) -> (File_Info, Maybe(Path_Error)) {
|
||||
_lstat :: proc(name: string, allocator := context.allocator) -> (File_Info, Error) {
|
||||
return internal_stat(name, win32.FILE_FLAG_BACKUP_SEMANTICS|win32.FILE_FLAG_OPEN_REPARSE_POINT)
|
||||
}
|
||||
_same_file :: proc(fi1, fi2: File_Info) -> bool {
|
||||
@@ -39,12 +40,12 @@ _same_file :: proc(fi1, fi2: File_Info) -> bool {
|
||||
|
||||
|
||||
|
||||
_stat_errno :: proc(errno: win32.DWORD) -> Path_Error {
|
||||
return Path_Error{err = Platform_Error{i32(errno)}}
|
||||
_stat_errno :: proc(errno: win32.DWORD) -> Error {
|
||||
return Platform_Error{i32(errno)}
|
||||
}
|
||||
|
||||
|
||||
full_path_from_name :: proc(name: string, allocator := context.allocator) -> (path: string, err: Maybe(Path_Error)) {
|
||||
full_path_from_name :: proc(name: string, allocator := context.allocator) -> (path: string, err: Error) {
|
||||
context.allocator = allocator
|
||||
|
||||
name := name
|
||||
@@ -69,15 +70,15 @@ full_path_from_name :: proc(name: string, allocator := context.allocator) -> (pa
|
||||
}
|
||||
|
||||
|
||||
internal_stat :: proc(name: string, create_file_attributes: u32, allocator := context.allocator) -> (fi: File_Info, e: Maybe(Path_Error)) {
|
||||
internal_stat :: proc(name: string, create_file_attributes: u32, allocator := context.allocator) -> (fi: File_Info, e: Error) {
|
||||
if len(name) == 0 {
|
||||
return {}, Path_Error{err = .Not_Exist}
|
||||
return {}, .Not_Exist
|
||||
}
|
||||
|
||||
context.allocator = allocator
|
||||
|
||||
|
||||
wname := win32.utf8_to_wstring(_fix_long_path(name), context.temp_allocator)
|
||||
wname := _fix_long_path(name)
|
||||
fa: win32.WIN32_FILE_ATTRIBUTE_DATA
|
||||
ok := win32.GetFileAttributesExW(wname, win32.GetFileExInfoStandard, &fa)
|
||||
if ok && fa.dwFileAttributes & win32.FILE_ATTRIBUTE_REPARSE_POINT == 0 {
|
||||
@@ -91,7 +92,7 @@ internal_stat :: proc(name: string, create_file_attributes: u32, allocator := co
|
||||
fd: win32.WIN32_FIND_DATAW
|
||||
sh := win32.FindFirstFileW(wname, &fd)
|
||||
if sh == win32.INVALID_HANDLE_VALUE {
|
||||
e = Path_Error{err = Platform_Error{i32(win32.GetLastError())}}
|
||||
e = _get_platform_error()
|
||||
return
|
||||
}
|
||||
win32.FindClose(sh)
|
||||
@@ -101,7 +102,7 @@ internal_stat :: proc(name: string, create_file_attributes: u32, allocator := co
|
||||
|
||||
h := win32.CreateFileW(wname, 0, 0, nil, win32.OPEN_EXISTING, create_file_attributes, nil)
|
||||
if h == win32.INVALID_HANDLE_VALUE {
|
||||
e = Path_Error{err = Platform_Error{i32(win32.GetLastError())}}
|
||||
e = _get_platform_error()
|
||||
return
|
||||
}
|
||||
defer win32.CloseHandle(h)
|
||||
@@ -130,9 +131,9 @@ _cleanpath_strip_prefix :: proc(buf: []u16) -> []u16 {
|
||||
}
|
||||
|
||||
|
||||
_cleanpath_from_handle :: proc(f: ^File) -> (string, Maybe(Path_Error)) {
|
||||
_cleanpath_from_handle :: proc(f: ^File) -> (string, Error) {
|
||||
if f == nil || f.impl.fd == nil {
|
||||
return "", Path_Error{err = .Invalid_Argument}
|
||||
return "", .Invalid_Argument
|
||||
}
|
||||
h := win32.HANDLE(f.impl.fd)
|
||||
|
||||
@@ -153,9 +154,9 @@ _cleanpath_from_handle :: proc(f: ^File) -> (string, Maybe(Path_Error)) {
|
||||
return _cleanpath_from_buf(buf), nil
|
||||
}
|
||||
|
||||
_cleanpath_from_handle_u16 :: proc(f: ^File) -> ([]u16, Maybe(Path_Error)) {
|
||||
_cleanpath_from_handle_u16 :: proc(f: ^File) -> ([]u16, Error) {
|
||||
if f == nil || f.impl.fd == nil {
|
||||
return nil, Path_Error{err = .Invalid_Argument}
|
||||
return nil, .Invalid_Argument
|
||||
}
|
||||
h := win32.HANDLE(f.impl.fd)
|
||||
|
||||
@@ -251,7 +252,7 @@ _file_mode_from_file_attributes :: proc(FileAttributes: win32.DWORD, h: win32.HA
|
||||
}
|
||||
|
||||
|
||||
_file_info_from_win32_file_attribute_data :: proc(d: ^win32.WIN32_FILE_ATTRIBUTE_DATA, name: string) -> (fi: File_Info, e: Maybe(Path_Error)) {
|
||||
_file_info_from_win32_file_attribute_data :: proc(d: ^win32.WIN32_FILE_ATTRIBUTE_DATA, name: string) -> (fi: File_Info, e: Error) {
|
||||
fi.size = i64(d.nFileSizeHigh)<<32 + i64(d.nFileSizeLow)
|
||||
|
||||
fi.mode |= _file_mode_from_file_attributes(d.dwFileAttributes, nil, 0)
|
||||
@@ -268,7 +269,7 @@ _file_info_from_win32_file_attribute_data :: proc(d: ^win32.WIN32_FILE_ATTRIBUTE
|
||||
}
|
||||
|
||||
|
||||
_file_info_from_win32_find_data :: proc(d: ^win32.WIN32_FIND_DATAW, name: string) -> (fi: File_Info, e: Maybe(Path_Error)) {
|
||||
_file_info_from_win32_find_data :: proc(d: ^win32.WIN32_FIND_DATAW, name: string) -> (fi: File_Info, e: Error) {
|
||||
fi.size = i64(d.nFileSizeHigh)<<32 + i64(d.nFileSizeLow)
|
||||
|
||||
fi.mode |= _file_mode_from_file_attributes(d.dwFileAttributes, nil, 0)
|
||||
@@ -285,7 +286,7 @@ _file_info_from_win32_find_data :: proc(d: ^win32.WIN32_FIND_DATAW, name: string
|
||||
}
|
||||
|
||||
|
||||
_file_info_from_get_file_information_by_handle :: proc(path: string, h: win32.HANDLE) -> (File_Info, Maybe(Path_Error)) {
|
||||
_file_info_from_get_file_information_by_handle :: proc(path: string, h: win32.HANDLE) -> (File_Info, Error) {
|
||||
d: win32.BY_HANDLE_FILE_INFORMATION
|
||||
if !win32.GetFileInformationByHandle(h, &d) {
|
||||
return {}, _stat_errno(win32.GetLastError())
|
||||
@@ -318,58 +319,83 @@ _file_info_from_get_file_information_by_handle :: proc(path: string, h: win32.HA
|
||||
return fi, nil
|
||||
}
|
||||
|
||||
_is_abs :: proc(path: string) -> bool {
|
||||
if len(path) > 0 && path[0] == '/' {
|
||||
return true
|
||||
|
||||
|
||||
reserved_names := [?]string{
|
||||
"CON", "PRN", "AUX", "NUL",
|
||||
"COM1", "COM2", "COM3", "COM4", "COM5", "COM6", "COM7", "COM8", "COM9",
|
||||
"LPT1", "LPT2", "LPT3", "LPT4", "LPT5", "LPT6", "LPT7", "LPT8", "LPT9",
|
||||
}
|
||||
|
||||
_is_reserved_name :: proc(path: string) -> bool {
|
||||
if len(path) == 0 {
|
||||
return false
|
||||
}
|
||||
if len(path) > 2 {
|
||||
switch path[0] {
|
||||
case 'A'..='Z', 'a'..='z':
|
||||
return path[1] == ':' && is_path_separator(path[2])
|
||||
for reserved in reserved_names {
|
||||
if strings.equal_fold(path, reserved) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
_fix_long_path :: proc(path: string) -> string {
|
||||
if len(path) < 248 {
|
||||
return path
|
||||
}
|
||||
_is_UNC :: proc(path: string) -> bool {
|
||||
return _volume_name_len(path) > 2
|
||||
}
|
||||
|
||||
if len(path) >= 2 && path[:2] == `\\` {
|
||||
return path
|
||||
}
|
||||
if !_is_abs(path) {
|
||||
return path
|
||||
}
|
||||
_volume_name_len :: proc(path: string) -> int {
|
||||
if ODIN_OS == .Windows {
|
||||
if len(path) < 2 {
|
||||
return 0
|
||||
}
|
||||
c := path[0]
|
||||
if path[1] == ':' {
|
||||
switch c {
|
||||
case 'a'..='z', 'A'..='Z':
|
||||
return 2
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
// URL: https://msdn.microsoft.com/en-us/library/windows/desktop/aa365247(v=vs.85).aspx
|
||||
if l := len(path); l >= 5 && _is_path_separator(path[0]) && _is_path_separator(path[1]) &&
|
||||
!_is_path_separator(path[2]) && path[2] != '.' {
|
||||
for n := 3; n < l-1; n += 1 {
|
||||
if _is_path_separator(path[n]) {
|
||||
n += 1
|
||||
if !_is_path_separator(path[n]) {
|
||||
if path[n] == '.' {
|
||||
break
|
||||
}
|
||||
}
|
||||
for ; n < l; n += 1 {
|
||||
if _is_path_separator(path[n]) {
|
||||
break
|
||||
}
|
||||
}
|
||||
return n
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if w == len(`\\?\c:`) {
|
||||
path_buf[w] = '\\'
|
||||
w += 1
|
||||
}
|
||||
return string(path_buf[:w])
|
||||
return 0
|
||||
}
|
||||
|
||||
|
||||
_is_abs :: proc(path: string) -> bool {
|
||||
if _is_reserved_name(path) {
|
||||
return true
|
||||
}
|
||||
l := _volume_name_len(path)
|
||||
if l == 0 {
|
||||
return false
|
||||
}
|
||||
|
||||
path := path
|
||||
path = path[l:]
|
||||
if path == "" {
|
||||
return false
|
||||
}
|
||||
return is_path_separator(path[0])
|
||||
}
|
||||
|
||||
|
||||
@@ -30,7 +30,7 @@ Monitor_Enum_Proc :: distinct #type proc "std" (Hmonitor, Hdc, ^Rect, Lparam) ->
|
||||
|
||||
Bool :: distinct b32
|
||||
|
||||
Wstring :: distinct ^u16
|
||||
Wstring :: distinct [^]u16
|
||||
|
||||
Point :: struct {
|
||||
x, y: i32,
|
||||
|
||||
Reference in New Issue
Block a user