authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2018-06-12 01:55:08-04:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2018-06-12 01:57:09-04:00
log3dd9af9948db696362aa5f41481dc4cb034bc6c2
tree747a0f203be57fc407d82ee6371602e897597661
parent0a18d53c3dc9816677071c20ab846e3866787b39

implement std.os.Dir for windows

improve std.os.File.access so that it does not depend on shlwapi.dll closes #1084

7 files changed, 293 insertions(+), 102 deletions(-)

doc/docgen.zig+2-8
......@@ -51,14 +51,8 @@ pub fn main() !void {
5151 var toc = try genToc(allocator, &tokenizer);
5252
5353 try os.makePath(allocator, tmp_dir_name);
54 defer {
55 // TODO issue #709
56 // disabled to pass CI tests, but obviously we want to implement this
57 // and then remove this workaround
58 if (builtin.os != builtin.Os.windows) {
59 os.deleteTree(allocator, tmp_dir_name) catch {};
60 }
61 }
54 defer os.deleteTree(allocator, tmp_dir_name) catch {};
55
6256 try genHtml(allocator, &tokenizer, &toc, &buffered_out_stream.stream, zig_exe);
6357 try buffered_out_stream.flush();
6458}
std/os/file.zig+15-3
......@@ -96,7 +96,20 @@ pub const File = struct {
9696 return File{ .handle = handle };
9797 }
9898
99 pub fn access(allocator: *mem.Allocator, path: []const u8, file_mode: os.FileMode) !bool {
99 pub const AccessError = error {
100 PermissionDenied,
101 NotFound,
102 NameTooLong,
103 BadMode,
104 BadPathName,
105 Io,
106 SystemResources,
107 OutOfMemory,
108
109 Unexpected,
110 };
111
112 pub fn access(allocator: *mem.Allocator, path: []const u8, file_mode: os.FileMode) AccessError!bool {
100113 const path_with_null = try std.cstr.addNullByte(allocator, path);
101114 defer allocator.free(path_with_null);
102115
......@@ -123,8 +136,7 @@ pub const File = struct {
123136 }
124137 return true;
125138 } else if (is_windows) {
126 // TODO do not depend on shlwapi.dll
127 if (os.windows.PathFileExistsA(path_with_null.ptr) == os.windows.TRUE) {
139 if (os.windows.GetFileAttributesA(path_with_null.ptr) != os.windows.INVALID_FILE_ATTRIBUTES) {
128140 return true;
129141 }
130142
std/os/index.zig+191-75
......@@ -734,7 +734,23 @@ pub fn atomicSymLink(allocator: *Allocator, existing_path: []const u8, new_path:
734734 }
735735}
736736
737pub fn deleteFile(allocator: *Allocator, file_path: []const u8) !void {
737pub const DeleteFileError = error {
738 FileNotFound,
739 AccessDenied,
740 FileBusy,
741 FileSystem,
742 IsDir,
743 SymLinkLoop,
744 NameTooLong,
745 NotDir,
746 SystemResources,
747 ReadOnlyFileSystem,
748 OutOfMemory,
749
750 Unexpected,
751};
752
753pub fn deleteFile(allocator: *Allocator, file_path: []const u8) DeleteFileError!void {
738754 if (builtin.os == Os.windows) {
739755 return deleteFileWindows(allocator, file_path);
740756 } else {
......@@ -1019,37 +1035,67 @@ pub fn makePath(allocator: *Allocator, full_path: []const u8) !void {
10191035 }
10201036}
10211037
1038pub const DeleteDirError = error {
1039 AccessDenied,
1040 FileBusy,
1041 SymLinkLoop,
1042 NameTooLong,
1043 FileNotFound,
1044 SystemResources,
1045 NotDir,
1046 DirNotEmpty,
1047 ReadOnlyFileSystem,
1048 OutOfMemory,
1049
1050 Unexpected,
1051};
1052
10221053/// Returns ::error.DirNotEmpty if the directory is not empty.
10231054/// To delete a directory recursively, see ::deleteTree
1024pub fn deleteDir(allocator: *Allocator, dir_path: []const u8) !void {
1055pub fn deleteDir(allocator: *Allocator, dir_path: []const u8) DeleteDirError!void {
10251056 const path_buf = try allocator.alloc(u8, dir_path.len + 1);
10261057 defer allocator.free(path_buf);
10271058
10281059 mem.copy(u8, path_buf, dir_path);
10291060 path_buf[dir_path.len] = 0;
10301061
1031 const err = posix.getErrno(posix.rmdir(path_buf.ptr));
1032 if (err > 0) {
1033 return switch (err) {
1034 posix.EACCES, posix.EPERM => error.AccessDenied,
1035 posix.EBUSY => error.FileBusy,
1036 posix.EFAULT, posix.EINVAL => unreachable,
1037 posix.ELOOP => error.SymLinkLoop,
1038 posix.ENAMETOOLONG => error.NameTooLong,
1039 posix.ENOENT => error.FileNotFound,
1040 posix.ENOMEM => error.SystemResources,
1041 posix.ENOTDIR => error.NotDir,
1042 posix.EEXIST, posix.ENOTEMPTY => error.DirNotEmpty,
1043 posix.EROFS => error.ReadOnlyFileSystem,
1044 else => unexpectedErrorPosix(err),
1045 };
1062 switch (builtin.os) {
1063 Os.windows => {
1064 if (windows.RemoveDirectoryA(path_buf.ptr) == 0) {
1065 const err = windows.GetLastError();
1066 return switch (err) {
1067 windows.ERROR.PATH_NOT_FOUND => error.FileNotFound,
1068 windows.ERROR.DIR_NOT_EMPTY => error.DirNotEmpty,
1069 else => unexpectedErrorWindows(err),
1070 };
1071 }
1072 },
1073 Os.linux, Os.macosx, Os.ios => {
1074 const err = posix.getErrno(posix.rmdir(path_buf.ptr));
1075 if (err > 0) {
1076 return switch (err) {
1077 posix.EACCES, posix.EPERM => error.AccessDenied,
1078 posix.EBUSY => error.FileBusy,
1079 posix.EFAULT, posix.EINVAL => unreachable,
1080 posix.ELOOP => error.SymLinkLoop,
1081 posix.ENAMETOOLONG => error.NameTooLong,
1082 posix.ENOENT => error.FileNotFound,
1083 posix.ENOMEM => error.SystemResources,
1084 posix.ENOTDIR => error.NotDir,
1085 posix.EEXIST, posix.ENOTEMPTY => error.DirNotEmpty,
1086 posix.EROFS => error.ReadOnlyFileSystem,
1087 else => unexpectedErrorPosix(err),
1088 };
1089 }
1090 },
1091 else => @compileError("unimplemented"),
10461092 }
1093
10471094}
10481095
10491096/// Whether ::full_path describes a symlink, file, or directory, this function
10501097/// removes it. If it cannot be removed because it is a non-empty directory,
10511098/// this function recursively removes its entries and then tries again.
1052/// TODO non-recursive implementation
10531099const DeleteTreeError = error{
10541100 OutOfMemory,
10551101 AccessDenied,
......@@ -1128,7 +1174,7 @@ pub fn deleteTree(allocator: *Allocator, full_path: []const u8) DeleteTreeError!
11281174 try full_entry_buf.resize(full_path.len + entry.name.len + 1);
11291175 const full_entry_path = full_entry_buf.toSlice();
11301176 mem.copy(u8, full_entry_path, full_path);
1131 full_entry_path[full_path.len] = '/';
1177 full_entry_path[full_path.len] = path.sep;
11321178 mem.copy(u8, full_entry_path[full_path.len + 1 ..], entry.name);
11331179
11341180 try deleteTree(allocator, full_entry_path);
......@@ -1139,16 +1185,29 @@ pub fn deleteTree(allocator: *Allocator, full_path: []const u8) DeleteTreeError!
11391185}
11401186
11411187pub const Dir = struct {
1142 fd: i32,
1143 darwin_seek: darwin_seek_t,
1188 handle: Handle,
11441189 allocator: *Allocator,
1145 buf: []u8,
1146 index: usize,
1147 end_index: usize,
11481190
1149 const darwin_seek_t = switch (builtin.os) {
1150 Os.macosx, Os.ios => i64,
1151 else => void,
1191 pub const Handle = switch (builtin.os) {
1192 Os.macosx, Os.ios => struct {
1193 fd: i32,
1194 seek: i64,
1195 buf: []u8,
1196 index: usize,
1197 end_index: usize,
1198 },
1199 Os.linux => struct {
1200 fd: i32,
1201 buf: []u8,
1202 index: usize,
1203 end_index: usize,
1204 },
1205 Os.windows => struct {
1206 handle: windows.HANDLE,
1207 find_file_data: windows.WIN32_FIND_DATAA,
1208 first: bool,
1209 },
1210 else => @compileError("unimplemented"),
11521211 };
11531212
11541213 pub const Entry = struct {
......@@ -1168,81 +1227,117 @@ pub const Dir = struct {
11681227 };
11691228 };
11701229
1171 pub fn open(allocator: *Allocator, dir_path: []const u8) !Dir {
1172 const fd = switch (builtin.os) {
1173 Os.windows => @compileError("TODO support Dir.open for windows"),
1174 Os.linux => try posixOpen(allocator, dir_path, posix.O_RDONLY | posix.O_DIRECTORY | posix.O_CLOEXEC, 0),
1175 Os.macosx, Os.ios => try posixOpen(
1176 allocator,
1177 dir_path,
1178 posix.O_RDONLY | posix.O_NONBLOCK | posix.O_DIRECTORY | posix.O_CLOEXEC,
1179 0,
1180 ),
1181 else => @compileError("Dir.open is not supported for this platform"),
1182 };
1183 const darwin_seek_init = switch (builtin.os) {
1184 Os.macosx, Os.ios => 0,
1185 else => {},
1186 };
1230 pub const OpenError = error {
1231 PathNotFound,
1232 NotDir,
1233 AccessDenied,
1234 FileTooBig,
1235 IsDir,
1236 SymLinkLoop,
1237 ProcessFdQuotaExceeded,
1238 NameTooLong,
1239 SystemFdQuotaExceeded,
1240 NoDevice,
1241 SystemResources,
1242 NoSpaceLeft,
1243 PathAlreadyExists,
1244 OutOfMemory,
1245
1246 Unexpected,
1247 };
1248
1249 pub fn open(allocator: *Allocator, dir_path: []const u8) OpenError!Dir {
11871250 return Dir{
11881251 .allocator = allocator,
1189 .fd = fd,
1190 .darwin_seek = darwin_seek_init,
1191 .index = 0,
1192 .end_index = 0,
1193 .buf = []u8{},
1252 .handle = switch (builtin.os) {
1253 Os.windows => blk: {
1254 var find_file_data: windows.WIN32_FIND_DATAA = undefined;
1255 const handle = try windows_util.windowsFindFirstFile(allocator, dir_path, &find_file_data);
1256 break :blk Handle {
1257 .handle = handle,
1258 .find_file_data = find_file_data, // TODO guaranteed copy elision
1259 .first = true,
1260 };
1261 },
1262 Os.macosx, Os.ios => Handle {
1263 .fd = try posixOpen(
1264 allocator,
1265 dir_path,
1266 posix.O_RDONLY | posix.O_NONBLOCK | posix.O_DIRECTORY | posix.O_CLOEXEC,
1267 0,
1268 ),
1269 .seek = 0,
1270 .index = 0,
1271 .end_index = 0,
1272 .buf = []u8{},
1273 },
1274 Os.linux => Handle {
1275 .fd = try posixOpen(allocator, dir_path, posix.O_RDONLY | posix.O_DIRECTORY | posix.O_CLOEXEC, 0,),
1276 .index = 0,
1277 .end_index = 0,
1278 .buf = []u8{},
1279 },
1280 else => @compileError("unimplemented"),
1281 },
11941282 };
11951283 }
11961284
11971285 pub fn close(self: *Dir) void {
1198 self.allocator.free(self.buf);
1199 os.close(self.fd);
1286 switch (builtin.os) {
1287 Os.windows => {
1288 _ = windows.FindClose(self.handle.handle);
1289 },
1290 Os.macosx, Os.ios, Os.linux => {
1291 self.allocator.free(self.handle.buf);
1292 os.close(self.handle.fd);
1293 },
1294 else => @compileError("unimplemented"),
1295 }
12001296 }
12011297
12021298 /// Memory such as file names referenced in this returned entry becomes invalid
1203 /// with subsequent calls to next, as well as when this ::Dir is deinitialized.
1299 /// with subsequent calls to next, as well as when this `Dir` is deinitialized.
12041300 pub fn next(self: *Dir) !?Entry {
12051301 switch (builtin.os) {
12061302 Os.linux => return self.nextLinux(),
12071303 Os.macosx, Os.ios => return self.nextDarwin(),
12081304 Os.windows => return self.nextWindows(),
1209 else => @compileError("Dir.next not supported on " ++ @tagName(builtin.os)),
1305 else => @compileError("unimplemented"),
12101306 }
12111307 }
12121308
12131309 fn nextDarwin(self: *Dir) !?Entry {
12141310 start_over: while (true) {
1215 if (self.index >= self.end_index) {
1216 if (self.buf.len == 0) {
1217 self.buf = try self.allocator.alloc(u8, page_size);
1311 if (self.handle.index >= self.handle.end_index) {
1312 if (self.handle.buf.len == 0) {
1313 self.handle.buf = try self.allocator.alloc(u8, page_size);
12181314 }
12191315
12201316 while (true) {
1221 const result = posix.getdirentries64(self.fd, self.buf.ptr, self.buf.len, &self.darwin_seek);
1317 const result = posix.getdirentries64(self.handle.fd, self.handle.buf.ptr, self.handle.buf.len, &self.handle.seek);
12221318 const err = posix.getErrno(result);
12231319 if (err > 0) {
12241320 switch (err) {
12251321 posix.EBADF, posix.EFAULT, posix.ENOTDIR => unreachable,
12261322 posix.EINVAL => {
1227 self.buf = try self.allocator.realloc(u8, self.buf, self.buf.len * 2);
1323 self.handle.buf = try self.allocator.realloc(u8, self.handle.buf, self.handle.buf.len * 2);
12281324 continue;
12291325 },
12301326 else => return unexpectedErrorPosix(err),
12311327 }
12321328 }
12331329 if (result == 0) return null;
1234 self.index = 0;
1235 self.end_index = result;
1330 self.handle.index = 0;
1331 self.handle.end_index = result;
12361332 break;
12371333 }
12381334 }
1239 const darwin_entry = @ptrCast(*align(1) posix.dirent, &self.buf[self.index]);
1240 const next_index = self.index + darwin_entry.d_reclen;
1241 self.index = next_index;
1335 const darwin_entry = @ptrCast(*align(1) posix.dirent, &self.handle.buf[self.handle.index]);
1336 const next_index = self.handle.index + darwin_entry.d_reclen;
1337 self.handle.index = next_index;
12421338
12431339 const name = @ptrCast([*]u8, &darwin_entry.d_name)[0..darwin_entry.d_namlen];
12441340
1245 // skip . and .. entries
12461341 if (mem.eql(u8, name, ".") or mem.eql(u8, name, "..")) {
12471342 continue :start_over;
12481343 }
......@@ -1266,38 +1361,59 @@ pub const Dir = struct {
12661361 }
12671362
12681363 fn nextWindows(self: *Dir) !?Entry {
1269 @compileError("TODO support Dir.next for windows");
1364 while (true) {
1365 if (self.handle.first) {
1366 self.handle.first = false;
1367 } else {
1368 if (!try windows_util.windowsFindNextFile(self.handle.handle, &self.handle.find_file_data))
1369 return null;
1370 }
1371 const name = std.cstr.toSlice(self.handle.find_file_data.cFileName[0..].ptr);
1372 if (mem.eql(u8, name, ".") or mem.eql(u8, name, ".."))
1373 continue;
1374 const kind = blk: {
1375 const attrs = self.handle.find_file_data.dwFileAttributes;
1376 if (attrs & windows.FILE_ATTRIBUTE_DIRECTORY != 0) break :blk Entry.Kind.Directory;
1377 if (attrs & windows.FILE_ATTRIBUTE_REPARSE_POINT != 0) break :blk Entry.Kind.SymLink;
1378 if (attrs & windows.FILE_ATTRIBUTE_NORMAL != 0) break :blk Entry.Kind.File;
1379 break :blk Entry.Kind.Unknown;
1380 };
1381 return Entry {
1382 .name = name,
1383 .kind = kind,
1384 };
1385 }
12701386 }
12711387
12721388 fn nextLinux(self: *Dir) !?Entry {
12731389 start_over: while (true) {
1274 if (self.index >= self.end_index) {
1275 if (self.buf.len == 0) {
1276 self.buf = try self.allocator.alloc(u8, page_size);
1390 if (self.handle.index >= self.handle.end_index) {
1391 if (self.handle.buf.len == 0) {
1392 self.handle.buf = try self.allocator.alloc(u8, page_size);
12771393 }
12781394
12791395 while (true) {
1280 const result = posix.getdents(self.fd, self.buf.ptr, self.buf.len);
1396 const result = posix.getdents(self.handle.fd, self.handle.buf.ptr, self.handle.buf.len);
12811397 const err = posix.getErrno(result);
12821398 if (err > 0) {
12831399 switch (err) {
12841400 posix.EBADF, posix.EFAULT, posix.ENOTDIR => unreachable,
12851401 posix.EINVAL => {
1286 self.buf = try self.allocator.realloc(u8, self.buf, self.buf.len * 2);
1402 self.handle.buf = try self.allocator.realloc(u8, self.handle.buf, self.handle.buf.len * 2);
12871403 continue;
12881404 },
12891405 else => return unexpectedErrorPosix(err),
12901406 }
12911407 }
12921408 if (result == 0) return null;
1293 self.index = 0;
1294 self.end_index = result;
1409 self.handle.index = 0;
1410 self.handle.end_index = result;
12951411 break;
12961412 }
12971413 }
1298 const linux_entry = @ptrCast(*align(1) posix.dirent, &self.buf[self.index]);
1299 const next_index = self.index + linux_entry.d_reclen;
1300 self.index = next_index;
1414 const linux_entry = @ptrCast(*align(1) posix.dirent, &self.handle.buf[self.handle.index]);
1415 const next_index = self.handle.index + linux_entry.d_reclen;
1416 self.handle.index = next_index;
13011417
13021418 const name = cstr.toSlice(@ptrCast([*]u8, &linux_entry.d_name));
13031419
......@@ -1306,7 +1422,7 @@ pub const Dir = struct {
13061422 continue :start_over;
13071423 }
13081424
1309 const type_char = self.buf[next_index - 1];
1425 const type_char = self.handle.buf[next_index - 1];
13101426 const entry_kind = switch (type_char) {
13111427 posix.DT_BLK => Entry.Kind.BlockDevice,
13121428 posix.DT_CHR => Entry.Kind.CharacterDevice,
std/os/test.zig-9
......@@ -10,11 +10,6 @@ const AtomicRmwOp = builtin.AtomicRmwOp;
1010const AtomicOrder = builtin.AtomicOrder;
1111
1212test "makePath, put some files in it, deleteTree" {
13 if (builtin.os == builtin.Os.windows) {
14 // TODO implement os.Dir for windows
15 // https://github.com/ziglang/zig/issues/709
16 return;
17 }
1813 try os.makePath(a, "os_test_tmp/b/c");
1914 try io.writeFile(a, "os_test_tmp/b/c/file.txt", "nonsense");
2015 try io.writeFile(a, "os_test_tmp/b/file2.txt", "blah");
......@@ -27,10 +22,6 @@ test "makePath, put some files in it, deleteTree" {
2722}
2823
2924test "access file" {
30 if (builtin.os == builtin.Os.windows) {
31 return;
32 }
33
3425 try os.makePath(a, "os_test_tmp");
3526 if (os.File.access(a, "os_test_tmp/file.txt", os.default_file_mode)) |ok| {
3627 unreachable;
std/os/time.zig+4-2
......@@ -68,11 +68,13 @@ pub const milliTimestamp = switch (builtin.os) {
6868fn milliTimestampWindows() u64 {
6969 //FileTime has a granularity of 100 nanoseconds
7070 // and uses the NTFS/Windows epoch
71 var ft: i64 = undefined;
71 var ft: windows.FILETIME = undefined;
7272 windows.GetSystemTimeAsFileTime(&ft);
7373 const hns_per_ms = (ns_per_s / 100) / ms_per_s;
7474 const epoch_adj = epoch.windows * ms_per_s;
75 return u64(@divFloor(ft, hns_per_ms) + epoch_adj);
75
76 const ft64 = (u64(ft.dwHighDateTime) << 32) | ft.dwLowDateTime;
77 return @divFloor(ft64, hns_per_ms) - - epoch_adj;
7678}
7779
7880fn milliTimestampDarwin() u64 {
std/os/windows/index.zig+43-5
......@@ -1,3 +1,7 @@
1test "import" {
2 _ = @import("util.zig");
3}
4
15pub const ERROR = @import("error.zig");
26
37pub extern "advapi32" stdcallcc fn CryptAcquireContextA(
......@@ -61,6 +65,10 @@ pub extern "kernel32" stdcallcc fn DeleteFileA(lpFileName: LPCSTR) BOOL;
6165
6266pub extern "kernel32" stdcallcc fn ExitProcess(exit_code: UINT) noreturn;
6367
68pub extern "kernel32" stdcallcc fn FindFirstFileA(lpFileName: LPCSTR, lpFindFileData: *WIN32_FIND_DATAA) HANDLE;
69pub extern "kernel32" stdcallcc fn FindClose(hFindFile: HANDLE) BOOL;
70pub extern "kernel32" stdcallcc fn FindNextFileA(hFindFile: HANDLE, lpFindFileData: *WIN32_FIND_DATAA) BOOL;
71
6472pub extern "kernel32" stdcallcc fn FreeEnvironmentStringsA(penv: [*]u8) BOOL;
6573
6674pub extern "kernel32" stdcallcc fn GetCommandLineA() LPSTR;
......@@ -77,6 +85,8 @@ pub extern "kernel32" stdcallcc fn GetExitCodeProcess(hProcess: HANDLE, lpExitCo
7785
7886pub extern "kernel32" stdcallcc fn GetFileSizeEx(hFile: HANDLE, lpFileSize: *LARGE_INTEGER) BOOL;
7987
88pub extern "kernel32" stdcallcc fn GetFileAttributesA(lpFileName: LPCSTR) DWORD;
89
8090pub extern "kernel32" stdcallcc fn GetModuleFileNameA(hModule: ?HMODULE, lpFilename: LPSTR, nSize: DWORD) DWORD;
8191
8292pub extern "kernel32" stdcallcc fn GetLastError() DWORD;
......@@ -97,7 +107,7 @@ pub extern "kernel32" stdcallcc fn GetFinalPathNameByHandleA(
97107
98108pub extern "kernel32" stdcallcc fn GetProcessHeap() ?HANDLE;
99109
100pub extern "kernel32" stdcallcc fn GetSystemTimeAsFileTime(?*FILETIME) void;
110pub extern "kernel32" stdcallcc fn GetSystemTimeAsFileTime(*FILETIME) void;
101111
102112pub extern "kernel32" stdcallcc fn HeapCreate(flOptions: DWORD, dwInitialSize: SIZE_T, dwMaximumSize: SIZE_T) ?HANDLE;
103113pub extern "kernel32" stdcallcc fn HeapDestroy(hHeap: HANDLE) BOOL;
......@@ -131,6 +141,8 @@ pub extern "kernel32" stdcallcc fn ReadFile(
131141 in_out_lpOverlapped: ?*OVERLAPPED,
132142) BOOL;
133143
144pub extern "kernel32" stdcallcc fn RemoveDirectoryA(lpPathName: LPCSTR) BOOL;
145
134146pub extern "kernel32" stdcallcc fn SetFilePointerEx(
135147 in_fFile: HANDLE,
136148 in_liDistanceToMove: LARGE_INTEGER,
......@@ -196,7 +208,6 @@ pub const UNICODE = false;
196208pub const WCHAR = u16;
197209pub const WORD = u16;
198210pub const LARGE_INTEGER = i64;
199pub const FILETIME = i64;
200211
201212pub const TRUE = 1;
202213pub const FALSE = 0;
......@@ -212,6 +223,8 @@ pub const STD_ERROR_HANDLE = @maxValue(DWORD) - 12 + 1;
212223
213224pub const INVALID_HANDLE_VALUE = @intToPtr(HANDLE, @maxValue(usize));
214225
226pub const INVALID_FILE_ATTRIBUTES = DWORD(@maxValue(DWORD));
227
215228pub const OVERLAPPED = extern struct {
216229 Internal: ULONG_PTR,
217230 InternalHigh: ULONG_PTR,
......@@ -293,13 +306,24 @@ pub const OPEN_EXISTING = 3;
293306pub const TRUNCATE_EXISTING = 5;
294307
295308pub const FILE_ATTRIBUTE_ARCHIVE = 0x20;
309pub const FILE_ATTRIBUTE_COMPRESSED = 0x800;
310pub const FILE_ATTRIBUTE_DEVICE = 0x40;
311pub const FILE_ATTRIBUTE_DIRECTORY = 0x10;
296312pub const FILE_ATTRIBUTE_ENCRYPTED = 0x4000;
297313pub const FILE_ATTRIBUTE_HIDDEN = 0x2;
314pub const FILE_ATTRIBUTE_INTEGRITY_STREAM = 0x8000;
298315pub const FILE_ATTRIBUTE_NORMAL = 0x80;
316pub const FILE_ATTRIBUTE_NOT_CONTENT_INDEXED = 0x2000;
317pub const FILE_ATTRIBUTE_NO_SCRUB_DATA = 0x20000;
299318pub const FILE_ATTRIBUTE_OFFLINE = 0x1000;
300319pub const FILE_ATTRIBUTE_READONLY = 0x1;
320pub const FILE_ATTRIBUTE_RECALL_ON_DATA_ACCESS = 0x400000;
321pub const FILE_ATTRIBUTE_RECALL_ON_OPEN = 0x40000;
322pub const FILE_ATTRIBUTE_REPARSE_POINT = 0x400;
323pub const FILE_ATTRIBUTE_SPARSE_FILE = 0x200;
301324pub const FILE_ATTRIBUTE_SYSTEM = 0x4;
302325pub const FILE_ATTRIBUTE_TEMPORARY = 0x100;
326pub const FILE_ATTRIBUTE_VIRTUAL = 0x10000;
303327
304328pub const PROCESS_INFORMATION = extern struct {
305329 hProcess: HANDLE,
......@@ -372,6 +396,20 @@ pub const HEAP_NO_SERIALIZE = 0x00000001;
372396pub const PTHREAD_START_ROUTINE = extern fn (LPVOID) DWORD;
373397pub const LPTHREAD_START_ROUTINE = PTHREAD_START_ROUTINE;
374398
375test "import" {
376 _ = @import("util.zig");
377}
399pub const WIN32_FIND_DATAA = extern struct {
400 dwFileAttributes: DWORD,
401 ftCreationTime: FILETIME,
402 ftLastAccessTime: FILETIME,
403 ftLastWriteTime: FILETIME,
404 nFileSizeHigh: DWORD,
405 nFileSizeLow: DWORD,
406 dwReserved0: DWORD,
407 dwReserved1: DWORD,
408 cFileName: [260]CHAR,
409 cAlternateFileName: [14]CHAR,
410};
411
412pub const FILETIME = extern struct {
413 dwLowDateTime: DWORD,
414 dwHighDateTime: DWORD,
415};
std/os/windows/util.zig+38
......@@ -170,3 +170,41 @@ test "InvalidDll" {
170170 return;
171171 };
172172}
173
174
175pub fn windowsFindFirstFile(allocator: *mem.Allocator, dir_path: []const u8,
176 find_file_data: *windows.WIN32_FIND_DATAA) !windows.HANDLE
177{
178 const wild_and_null = []u8{'\\', '*', 0};
179 const path_with_wild_and_null = try allocator.alloc(u8, dir_path.len + wild_and_null.len);
180 defer allocator.free(path_with_wild_and_null);
181
182 mem.copy(u8, path_with_wild_and_null, dir_path);
183 mem.copy(u8, path_with_wild_and_null[dir_path.len..], wild_and_null);
184
185 const handle = windows.FindFirstFileA(path_with_wild_and_null.ptr, find_file_data);
186
187 if (handle == windows.INVALID_HANDLE_VALUE) {
188 const err = windows.GetLastError();
189 switch (err) {
190 windows.ERROR.FILE_NOT_FOUND,
191 windows.ERROR.PATH_NOT_FOUND,
192 => return error.PathNotFound,
193 else => return os.unexpectedErrorWindows(err),
194 }
195 }
196
197 return handle;
198}
199
200/// Returns `true` if there was another file, `false` otherwise.
201pub fn windowsFindNextFile(handle: windows.HANDLE, find_file_data: *windows.WIN32_FIND_DATAA) !bool {
202 if (windows.FindNextFileA(handle, find_file_data) == 0) {
203 const err = windows.GetLastError();
204 return switch (err) {
205 windows.ERROR.NO_MORE_FILES => false,
206 else => os.unexpectedErrorWindows(err),
207 };
208 }
209 return true;
210}