diff --git a/core/os/os2/errors.odin b/core/os/os2/errors.odin index 69aeaee4b..6ddae74b1 100644 --- a/core/os/os2/errors.odin +++ b/core/os/os2/errors.odin @@ -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) { diff --git a/core/os/os2/file.odin b/core/os/os2/file.odin index 463d27c54..0ed08c850 100644 --- a/core/os/os2/file.odin +++ b/core/os/os2/file.odin @@ -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) } diff --git a/core/os/os2/file_util.odin b/core/os/os2/file_util.odin index 5ff4a6b99..f82bf73d0 100644 --- a/core/os/os2/file_util.odin +++ b/core/os/os2/file_util.odin @@ -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 } diff --git a/core/os/os2/file_windows.odin b/core/os/os2/file_windows.odin index a2ecaab2f..880305830 100644 --- a/core/os/os2/file_windows.odin +++ b/core/os/os2/file_windows.odin @@ -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)) { - -} diff --git a/core/os/os2/path.odin b/core/os/os2/path.odin index ee7d6e6f2..eca8b518f 100644 --- a/core/os/os2/path.odin +++ b/core/os/os2/path.odin @@ -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) } diff --git a/core/os/os2/path_windows.odin b/core/os/os2/path_windows.odin index 607f56968..0e77db4fd 100644 --- a/core/os/os2/path_windows.odin +++ b/core/os/os2/path_windows.odin @@ -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]) + +} diff --git a/core/os/os2/stat.odin b/core/os/os2/stat.odin index aa60e2a62..63f5a17e8 100644 --- a/core/os/os2/stat.odin +++ b/core/os/os2/stat.odin @@ -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) } diff --git a/core/os/os2/stat_windows.odin b/core/os/os2/stat_windows.odin index 5ee9e90a1..a79e5aae2 100644 --- a/core/os/os2/stat_windows.odin +++ b/core/os/os2/stat_windows.odin @@ -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]) +} + diff --git a/core/sys/win32/general.odin b/core/sys/win32/general.odin index d53bf8a4f..1baad8b11 100644 --- a/core/sys/win32/general.odin +++ b/core/sys/win32/general.odin @@ -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,