瀏覽代碼

Add `os.stat`, `os.lstat`, `os.fstat`, `filepath.walk`

gingerBill 5 年之前
父節點
當前提交
9ae3879956

+ 1 - 1
core/os/dir_windows.odin

@@ -57,7 +57,7 @@ read_dir :: proc(fd: Handle, n: int, allocator := context.allocator) -> (fi: []F
 
 
 	h := win32.HANDLE(fd);
 	h := win32.HANDLE(fd);
 
 
-	dir_fi, _ := stat_from_file_information("", h);
+	dir_fi, _ := file_info_from_get_file_information_by_handle("", h);
 	if !dir_fi.is_dir {
 	if !dir_fi.is_dir {
 		return nil, ERROR_FILE_IS_NOT_DIR;
 		return nil, ERROR_FILE_IS_NOT_DIR;
 	}
 	}

+ 9 - 2
core/os/stat.odin

@@ -14,8 +14,15 @@ File_Info :: struct {
 	access_time:       time.Time,
 	access_time:       time.Time,
 }
 }
 
 
-file_info_delete :: proc(fi: File_Info) {
-	delete(fi.fullpath);
+file_info_slice_delete :: proc(infos: []File_Info, allocator := context.allocator) {
+	for i := len(infos)-1; i >= 0; i -= 1 {
+		file_info_delete(infos[i], allocator);
+	}
+	delete(infos, allocator);
+}
+
+file_info_delete :: proc(fi: File_Info, allocator := context.allocator) {
+	delete(fi.fullpath, allocator);
 }
 }
 
 
 File_Mode :: distinct u32;
 File_Mode :: distinct u32;

+ 148 - 26
core/os/stat_windows.odin

@@ -3,10 +3,88 @@ package os
 import "core:time"
 import "core:time"
 import win32 "core:sys/windows"
 import win32 "core:sys/windows"
 
 
-stat :: proc(fd: Handle) -> (File_Info, Errno) {
+@(private)
+full_path_from_name :: proc(name: string, allocator := context.allocator) -> (path: string, err: Errno) {
+	name := name;
+	if name == "" {
+		name = ".";
+	}
+	p := win32.utf8_to_utf16(name, context.temp_allocator);
+	defer delete(p);
+	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 "", Errno(win32.GetLastError());
+		}
+		if n <= u32(len(buf)) {
+			return win32.utf16_to_utf8(buf[:n]), ERROR_NONE;
+		}
+		resize(&buf, len(buf)*2);
+	}
+
+	return;
+}
+
+@(private)
+_stat :: proc(name: string, create_file_attributes: u32, allocator := context.allocator) -> (fi: File_Info, e: Errno) {
+	if len(name) == 0 {
+		return {}, ERROR_PATH_NOT_FOUND;
+	}
+
+	context.allocator = allocator;
+
+
+	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 = Errno(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 = Errno(win32.GetLastError());
+		return;
+	}
+	defer win32.CloseHandle(h);
+	return file_info_from_get_file_information_by_handle(name, h);
+}
+
+
+lstat :: proc(name: string, allocator := context.allocator) -> (File_Info, Errno) {
+	attrs := u32(win32.FILE_FLAG_BACKUP_SEMANTICS);
+	attrs |= win32.FILE_FLAG_OPEN_REPARSE_POINT;
+	return _stat(name, attrs, allocator);
+}
+
+stat :: proc(name: string, allocator := context.allocator) -> (File_Info, Errno) {
+	attrs := u32(win32.FILE_FLAG_BACKUP_SEMANTICS);
+	return _stat(name, attrs, allocator);
+}
+
+fstat :: proc(fd: Handle, allocator := context.allocator) -> (File_Info, Errno) {
 	if fd == 0 {
 	if fd == 0 {
 		return {}, ERROR_INVALID_HANDLE;
 		return {}, ERROR_INVALID_HANDLE;
 	}
 	}
+	context.allocator = allocator;
+
 	path, err := cleanpath_from_handle(fd);
 	path, err := cleanpath_from_handle(fd);
 	if err != ERROR_NONE {
 	if err != ERROR_NONE {
 		return {}, err;
 		return {}, err;
@@ -22,7 +100,7 @@ stat :: proc(fd: Handle) -> (File_Info, Errno) {
 		return fi, ERROR_NONE;
 		return fi, ERROR_NONE;
 	}
 	}
 
 
-	return stat_from_file_information(path, h);
+	return file_info_from_get_file_information_by_handle(path, h);
 }
 }
 
 
 
 
@@ -137,8 +215,73 @@ file_type_mode :: proc(h: win32.HANDLE) -> File_Mode {
 	return 0;
 	return 0;
 }
 }
 
 
+
+@(private)
+file_mode_from_file_attributes :: proc(FileAttributes: win32.DWORD, h: win32.HANDLE, ReparseTag: win32.DWORD) -> (mode: File_Mode) {
+	if FileAttributes & win32.FILE_ATTRIBUTE_READONLY != 0 {
+		mode |= 0o444;
+	} else {
+		mode |= 0o666;
+	}
+
+	is_sym := false;
+	if FileAttributes & win32.FILE_ATTRIBUTE_REPARSE_POINT == 0 {
+		is_sym = false;
+	} else {
+		is_sym = ReparseTag == win32.IO_REPARSE_TAG_SYMLINK || ReparseTag == win32.IO_REPARSE_TAG_MOUNT_POINT;
+	}
+
+	if is_sym {
+		mode |= File_Mode_Sym_Link;
+	} else {
+		if FileAttributes & win32.FILE_ATTRIBUTE_DIRECTORY != 0 {
+			mode |= 0o111 | File_Mode_Dir;
+		}
+
+		if h != nil {
+			mode |= file_type_mode(h);
+		}
+	}
+
+	return;
+}
+
 @(private)
 @(private)
-stat_from_file_information :: proc(path: string, h: win32.HANDLE) -> (File_Info, Errno) {
+file_info_from_win32_file_attribute_data :: proc(d: ^win32.WIN32_FILE_ATTRIBUTE_DATA, name: string) -> (fi: File_Info, e: Errno) {
+	fi.size = i64(d.nFileSizeHigh)<<32 + i64(d.nFileSizeLow);
+
+	fi.mode |= file_mode_from_file_attributes(d.dwFileAttributes, nil, 0);
+	fi.is_dir = fi.mode & File_Mode_Dir != 0;
+
+	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;
+}
+
+@(private)
+file_info_from_win32_find_data :: proc(d: ^win32.WIN32_FIND_DATAW, name: string) -> (fi: File_Info, e: Errno) {
+	fi.size = i64(d.nFileSizeHigh)<<32 + i64(d.nFileSizeLow);
+
+	fi.mode |= file_mode_from_file_attributes(d.dwFileAttributes, nil, 0);
+	fi.is_dir = fi.mode & File_Mode_Dir != 0;
+
+	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;
+}
+
+@(private)
+file_info_from_get_file_information_by_handle :: proc(path: string, h: win32.HANDLE) -> (File_Info, Errno) {
 	d: win32.BY_HANDLE_FILE_INFORMATION;
 	d: win32.BY_HANDLE_FILE_INFORMATION;
 	if !win32.GetFileInformationByHandle(h, &d) {
 	if !win32.GetFileInformationByHandle(h, &d) {
 		err := Errno(win32.GetLastError());
 		err := Errno(win32.GetLastError());
@@ -162,34 +305,13 @@ stat_from_file_information :: proc(path: string, h: win32.HANDLE) -> (File_Info,
 	fi.name = basename(path);
 	fi.name = basename(path);
 	fi.size = i64(d.nFileSizeHigh)<<32 + i64(d.nFileSizeLow);
 	fi.size = i64(d.nFileSizeHigh)<<32 + i64(d.nFileSizeLow);
 
 
-	if ti.FileAttributes & win32.FILE_ATTRIBUTE_READONLY != 0 {
-		fi.mode |= 0o444;
-	} else {
-		fi.mode |= 0o666;
-	}
-
-	is_sym := false;
-	if ti.FileAttributes & win32.FILE_ATTRIBUTE_REPARSE_Point == 0 {
-		is_sym = false;
-	} else {
-		is_sym = ti.ReparseTag == win32.IO_REPARSE_TAG_SYMLINK || ti.ReparseTag == win32.IO_REPARSE_TAG_MOUNT_POINT;
-	}
-
-	if is_sym {
-		fi.mode |= File_Mode_Sym_Link;
-	} else {
-		if ti.FileAttributes & win32.FILE_ATTRIBUTE_DIRECTORY != 0 {
-			fi.mode |= 0o111 | File_Mode_Dir;
-		}
-
-		fi.mode |= file_type_mode(h);
-	}
+	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.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.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.access_time       = time.unix(0, win32.FILETIME_as_unix_nanoseconds(d.ftLastAccessTime));
 
 
-	fi.is_dir = fi.mode & File_Mode_Dir != 0;
 
 
 	return fi, ERROR_NONE;
 	return fi, ERROR_NONE;
 }
 }

+ 3 - 3
core/path/filepath/match.odin

@@ -83,8 +83,8 @@ scan_chunk :: proc(pattern: string) -> (star: bool, chunk, rest: string) {
 		pattern = pattern[1:];
 		pattern = pattern[1:];
 		star = true;
 		star = true;
 	}
 	}
-	in_range := false;
-	i: int;
+
+	in_range, i := false, 0;
 
 
 	scan_loop: for i = 0; i < len(pattern); i += 1 {
 	scan_loop: for i = 0; i < len(pattern); i += 1 {
 		switch pattern[i] {
 		switch pattern[i] {
@@ -272,7 +272,7 @@ _glob :: proc(dir, pattern: string, matches: ^[dynamic]string) -> (m: [dynamic]s
 	}
 	}
 	defer os.close(d);
 	defer os.close(d);
 
 
-	fi, ferr := os.stat(d);
+	fi, ferr := os.fstat(d);
 	if ferr != 0 {
 	if ferr != 0 {
 		os.file_info_delete(fi);
 		os.file_info_delete(fi);
 		return;
 		return;

+ 2 - 0
core/path/filepath/path_windows.odin

@@ -68,6 +68,8 @@ full_path :: proc(name: string, allocator := context.allocator) -> (path: string
 		}
 		}
 		resize(&buf, len(buf)*2);
 		resize(&buf, len(buf)*2);
 	}
 	}
+
+	return;
 }
 }
 
 
 
 

+ 7 - 0
core/sys/windows/types.odin

@@ -189,22 +189,29 @@ ERROR_PATH_NOT_FOUND: DWORD : 3;
 ERROR_ACCESS_DENIED: DWORD : 5;
 ERROR_ACCESS_DENIED: DWORD : 5;
 ERROR_INVALID_HANDLE: DWORD : 6;
 ERROR_INVALID_HANDLE: DWORD : 6;
 ERROR_NO_MORE_FILES: DWORD : 18;
 ERROR_NO_MORE_FILES: DWORD : 18;
+ERROR_SHARING_VIOLATION: DWORD : 32;
+ERROR_LOCK_VIOLATION: DWORD : 33;
 ERROR_HANDLE_EOF: DWORD : 38;
 ERROR_HANDLE_EOF: DWORD : 38;
+ERROR_NOT_SUPPORTED: DWORD : 50;
 ERROR_FILE_EXISTS: DWORD : 80;
 ERROR_FILE_EXISTS: DWORD : 80;
 ERROR_INVALID_PARAMETER: DWORD : 87;
 ERROR_INVALID_PARAMETER: DWORD : 87;
 ERROR_BROKEN_PIPE: DWORD : 109;
 ERROR_BROKEN_PIPE: DWORD : 109;
 ERROR_CALL_NOT_IMPLEMENTED: DWORD : 120;
 ERROR_CALL_NOT_IMPLEMENTED: DWORD : 120;
 ERROR_INSUFFICIENT_BUFFER: DWORD : 122;
 ERROR_INSUFFICIENT_BUFFER: DWORD : 122;
+ERROR_INVALID_NAME: DWORD : 123;
+ERROR_LOCK_FAILED: DWORD : 167;
 ERROR_ALREADY_EXISTS: DWORD : 183;
 ERROR_ALREADY_EXISTS: DWORD : 183;
 ERROR_NO_DATA: DWORD : 232;
 ERROR_NO_DATA: DWORD : 232;
 ERROR_ENVVAR_NOT_FOUND: DWORD : 203;
 ERROR_ENVVAR_NOT_FOUND: DWORD : 203;
 ERROR_OPERATION_ABORTED: DWORD : 995;
 ERROR_OPERATION_ABORTED: DWORD : 995;
 ERROR_IO_PENDING: DWORD : 997;
 ERROR_IO_PENDING: DWORD : 997;
 ERROR_TIMEOUT: DWORD : 0x5B4;
 ERROR_TIMEOUT: DWORD : 0x5B4;
+ERROR_NO_UNICODE_TRANSLATION: DWORD : 1113;
 
 
 E_NOTIMPL :: HRESULT(-0x7fff_bfff); // 0x8000_4001
 E_NOTIMPL :: HRESULT(-0x7fff_bfff); // 0x8000_4001
 
 
 INVALID_HANDLE :: HANDLE(~uintptr(0));
 INVALID_HANDLE :: HANDLE(~uintptr(0));
+INVALID_HANDLE_VALUE :: INVALID_HANDLE;
 
 
 FACILITY_NT_BIT: DWORD : 0x1000_0000;
 FACILITY_NT_BIT: DWORD : 0x1000_0000;