浏览代码

Begin mocking os2 for windows out more

gingerBill 3 年之前
父节点
当前提交
96ab17ecfc

+ 2 - 30
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) {

+ 36 - 23
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)
 }
 

+ 1 - 1
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
 	}

+ 190 - 39
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
-}
-
-_open :: 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_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) {
-	return nil
-}
-
-_remove :: proc(name: string) -> 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
 }
 
-_rename :: proc(old_path, new_path: string) -> Maybe(Path_Error) {
+_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) -> 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) -> 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
 }
 
-
-_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)) {
+_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)) {
-
-}

+ 3 - 3
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)
 }
 

+ 83 - 3
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])
+
+}

+ 3 - 3
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)
 }
 

+ 86 - 60
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
 			}
 		}
 	}
+	return 0
+}
+
 
-	if w == len(`\\?\c:`) {
-		path_buf[w] = '\\'
-		w += 1
+_is_abs :: proc(path: string) -> bool {
+	if _is_reserved_name(path) {
+		return true
+	}
+	l := _volume_name_len(path)
+	if l == 0 {
+		return false
 	}
-	return string(path_buf[:w])
+
+	path := path
+	path = path[l:]
+	if path == "" {
+		return false
+	}
+	return is_path_separator(path[0])
 }
+

+ 1 - 1
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,