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 {...@@ -51,14 +51,8 @@ pub fn main() !void {
51 var toc = try genToc(allocator, &tokenizer);51 var toc = try genToc(allocator, &tokenizer);
5252
53 try os.makePath(allocator, tmp_dir_name);53 try os.makePath(allocator, tmp_dir_name);
54 defer {54 defer os.deleteTree(allocator, tmp_dir_name) catch {};
55 // TODO issue #70955
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 }
62 try genHtml(allocator, &tokenizer, &toc, &buffered_out_stream.stream, zig_exe);56 try genHtml(allocator, &tokenizer, &toc, &buffered_out_stream.stream, zig_exe);
63 try buffered_out_stream.flush();57 try buffered_out_stream.flush();
64}58}
std/os/file.zig+15-3
...@@ -96,7 +96,20 @@ pub const File = struct {...@@ -96,7 +96,20 @@ pub const File = struct {
96 return File{ .handle = handle };96 return File{ .handle = handle };
97 }97 }
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 {
100 const path_with_null = try std.cstr.addNullByte(allocator, path);113 const path_with_null = try std.cstr.addNullByte(allocator, path);
101 defer allocator.free(path_with_null);114 defer allocator.free(path_with_null);
102115
...@@ -123,8 +136,7 @@ pub const File = struct {...@@ -123,8 +136,7 @@ pub const File = struct {
123 }136 }
124 return true;137 return true;
125 } else if (is_windows) {138 } else if (is_windows) {
126 // TODO do not depend on shlwapi.dll139 if (os.windows.GetFileAttributesA(path_with_null.ptr) != os.windows.INVALID_FILE_ATTRIBUTES) {
127 if (os.windows.PathFileExistsA(path_with_null.ptr) == os.windows.TRUE) {
128 return true;140 return true;
129 }141 }
130142
std/os/index.zig+191-75
...@@ -734,7 +734,23 @@ pub fn atomicSymLink(allocator: *Allocator, existing_path: []const u8, new_path:...@@ -734,7 +734,23 @@ pub fn atomicSymLink(allocator: *Allocator, existing_path: []const u8, new_path:
734 }734 }
735}735}
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 {
738 if (builtin.os == Os.windows) {754 if (builtin.os == Os.windows) {
739 return deleteFileWindows(allocator, file_path);755 return deleteFileWindows(allocator, file_path);
740 } else {756 } else {
...@@ -1019,37 +1035,67 @@ pub fn makePath(allocator: *Allocator, full_path: []const u8) !void {...@@ -1019,37 +1035,67 @@ pub fn makePath(allocator: *Allocator, full_path: []const u8) !void {
1019 }1035 }
1020}1036}
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
1022/// Returns ::error.DirNotEmpty if the directory is not empty.1053/// Returns ::error.DirNotEmpty if the directory is not empty.
1023/// To delete a directory recursively, see ::deleteTree1054/// 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 {
1025 const path_buf = try allocator.alloc(u8, dir_path.len + 1);1056 const path_buf = try allocator.alloc(u8, dir_path.len + 1);
1026 defer allocator.free(path_buf);1057 defer allocator.free(path_buf);
10271058
1028 mem.copy(u8, path_buf, dir_path);1059 mem.copy(u8, path_buf, dir_path);
1029 path_buf[dir_path.len] = 0;1060 path_buf[dir_path.len] = 0;
10301061
1031 const err = posix.getErrno(posix.rmdir(path_buf.ptr));1062 switch (builtin.os) {
1032 if (err > 0) {1063 Os.windows => {
1033 return switch (err) {1064 if (windows.RemoveDirectoryA(path_buf.ptr) == 0) {
1034 posix.EACCES, posix.EPERM => error.AccessDenied,1065 const err = windows.GetLastError();
1035 posix.EBUSY => error.FileBusy,1066 return switch (err) {
1036 posix.EFAULT, posix.EINVAL => unreachable,1067 windows.ERROR.PATH_NOT_FOUND => error.FileNotFound,
1037 posix.ELOOP => error.SymLinkLoop,1068 windows.ERROR.DIR_NOT_EMPTY => error.DirNotEmpty,
1038 posix.ENAMETOOLONG => error.NameTooLong,1069 else => unexpectedErrorWindows(err),
1039 posix.ENOENT => error.FileNotFound,1070 };
1040 posix.ENOMEM => error.SystemResources,1071 }
1041 posix.ENOTDIR => error.NotDir,1072 },
1042 posix.EEXIST, posix.ENOTEMPTY => error.DirNotEmpty,1073 Os.linux, Os.macosx, Os.ios => {
1043 posix.EROFS => error.ReadOnlyFileSystem,1074 const err = posix.getErrno(posix.rmdir(path_buf.ptr));
1044 else => unexpectedErrorPosix(err),1075 if (err > 0) {
1045 };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"),
1046 }1092 }
1093
1047}1094}
10481095
1049/// Whether ::full_path describes a symlink, file, or directory, this function1096/// Whether ::full_path describes a symlink, file, or directory, this function
1050/// removes it. If it cannot be removed because it is a non-empty directory,1097/// removes it. If it cannot be removed because it is a non-empty directory,
1051/// this function recursively removes its entries and then tries again.1098/// this function recursively removes its entries and then tries again.
1052/// TODO non-recursive implementation
1053const DeleteTreeError = error{1099const DeleteTreeError = error{
1054 OutOfMemory,1100 OutOfMemory,
1055 AccessDenied,1101 AccessDenied,
...@@ -1128,7 +1174,7 @@ pub fn deleteTree(allocator: *Allocator, full_path: []const u8) DeleteTreeError!...@@ -1128,7 +1174,7 @@ pub fn deleteTree(allocator: *Allocator, full_path: []const u8) DeleteTreeError!
1128 try full_entry_buf.resize(full_path.len + entry.name.len + 1);1174 try full_entry_buf.resize(full_path.len + entry.name.len + 1);
1129 const full_entry_path = full_entry_buf.toSlice();1175 const full_entry_path = full_entry_buf.toSlice();
1130 mem.copy(u8, full_entry_path, full_path);1176 mem.copy(u8, full_entry_path, full_path);
1131 full_entry_path[full_path.len] = '/';1177 full_entry_path[full_path.len] = path.sep;
1132 mem.copy(u8, full_entry_path[full_path.len + 1 ..], entry.name);1178 mem.copy(u8, full_entry_path[full_path.len + 1 ..], entry.name);
11331179
1134 try deleteTree(allocator, full_entry_path);1180 try deleteTree(allocator, full_entry_path);
...@@ -1139,16 +1185,29 @@ pub fn deleteTree(allocator: *Allocator, full_path: []const u8) DeleteTreeError!...@@ -1139,16 +1185,29 @@ pub fn deleteTree(allocator: *Allocator, full_path: []const u8) DeleteTreeError!
1139}1185}
11401186
1141pub const Dir = struct {1187pub const Dir = struct {
1142 fd: i32,1188 handle: Handle,
1143 darwin_seek: darwin_seek_t,
1144 allocator: *Allocator,1189 allocator: *Allocator,
1145 buf: []u8,
1146 index: usize,
1147 end_index: usize,
11481190
1149 const darwin_seek_t = switch (builtin.os) {1191 pub const Handle = switch (builtin.os) {
1150 Os.macosx, Os.ios => i64,1192 Os.macosx, Os.ios => struct {
1151 else => void,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"),
1152 };1211 };
11531212
1154 pub const Entry = struct {1213 pub const Entry = struct {
...@@ -1168,81 +1227,117 @@ pub const Dir = struct {...@@ -1168,81 +1227,117 @@ pub const Dir = struct {
1168 };1227 };
1169 };1228 };
11701229
1171 pub fn open(allocator: *Allocator, dir_path: []const u8) !Dir {1230 pub const OpenError = error {
1172 const fd = switch (builtin.os) {1231 PathNotFound,
1173 Os.windows => @compileError("TODO support Dir.open for windows"),1232 NotDir,
1174 Os.linux => try posixOpen(allocator, dir_path, posix.O_RDONLY | posix.O_DIRECTORY | posix.O_CLOEXEC, 0),1233 AccessDenied,
1175 Os.macosx, Os.ios => try posixOpen(1234 FileTooBig,
1176 allocator,1235 IsDir,
1177 dir_path,1236 SymLinkLoop,
1178 posix.O_RDONLY | posix.O_NONBLOCK | posix.O_DIRECTORY | posix.O_CLOEXEC,1237 ProcessFdQuotaExceeded,
1179 0,1238 NameTooLong,
1180 ),1239 SystemFdQuotaExceeded,
1181 else => @compileError("Dir.open is not supported for this platform"),1240 NoDevice,
1182 };1241 SystemResources,
1183 const darwin_seek_init = switch (builtin.os) {1242 NoSpaceLeft,
1184 Os.macosx, Os.ios => 0,1243 PathAlreadyExists,
1185 else => {},1244 OutOfMemory,
1186 };1245
1246 Unexpected,
1247 };
1248
1249 pub fn open(allocator: *Allocator, dir_path: []const u8) OpenError!Dir {
1187 return Dir{1250 return Dir{
1188 .allocator = allocator,1251 .allocator = allocator,
1189 .fd = fd,1252 .handle = switch (builtin.os) {
1190 .darwin_seek = darwin_seek_init,1253 Os.windows => blk: {
1191 .index = 0,1254 var find_file_data: windows.WIN32_FIND_DATAA = undefined;
1192 .end_index = 0,1255 const handle = try windows_util.windowsFindFirstFile(allocator, dir_path, &find_file_data);
1193 .buf = []u8{},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 },
1194 };1282 };
1195 }1283 }
11961284
1197 pub fn close(self: *Dir) void {1285 pub fn close(self: *Dir) void {
1198 self.allocator.free(self.buf);1286 switch (builtin.os) {
1199 os.close(self.fd);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 }
1200 }1296 }
12011297
1202 /// Memory such as file names referenced in this returned entry becomes invalid1298 /// 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.
1204 pub fn next(self: *Dir) !?Entry {1300 pub fn next(self: *Dir) !?Entry {
1205 switch (builtin.os) {1301 switch (builtin.os) {
1206 Os.linux => return self.nextLinux(),1302 Os.linux => return self.nextLinux(),
1207 Os.macosx, Os.ios => return self.nextDarwin(),1303 Os.macosx, Os.ios => return self.nextDarwin(),
1208 Os.windows => return self.nextWindows(),1304 Os.windows => return self.nextWindows(),
1209 else => @compileError("Dir.next not supported on " ++ @tagName(builtin.os)),1305 else => @compileError("unimplemented"),
1210 }1306 }
1211 }1307 }
12121308
1213 fn nextDarwin(self: *Dir) !?Entry {1309 fn nextDarwin(self: *Dir) !?Entry {
1214 start_over: while (true) {1310 start_over: while (true) {
1215 if (self.index >= self.end_index) {1311 if (self.handle.index >= self.handle.end_index) {
1216 if (self.buf.len == 0) {1312 if (self.handle.buf.len == 0) {
1217 self.buf = try self.allocator.alloc(u8, page_size);1313 self.handle.buf = try self.allocator.alloc(u8, page_size);
1218 }1314 }
12191315
1220 while (true) {1316 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);
1222 const err = posix.getErrno(result);1318 const err = posix.getErrno(result);
1223 if (err > 0) {1319 if (err > 0) {
1224 switch (err) {1320 switch (err) {
1225 posix.EBADF, posix.EFAULT, posix.ENOTDIR => unreachable,1321 posix.EBADF, posix.EFAULT, posix.ENOTDIR => unreachable,
1226 posix.EINVAL => {1322 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);
1228 continue;1324 continue;
1229 },1325 },
1230 else => return unexpectedErrorPosix(err),1326 else => return unexpectedErrorPosix(err),
1231 }1327 }
1232 }1328 }
1233 if (result == 0) return null;1329 if (result == 0) return null;
1234 self.index = 0;1330 self.handle.index = 0;
1235 self.end_index = result;1331 self.handle.end_index = result;
1236 break;1332 break;
1237 }1333 }
1238 }1334 }
1239 const darwin_entry = @ptrCast(*align(1) posix.dirent, &self.buf[self.index]);1335 const darwin_entry = @ptrCast(*align(1) posix.dirent, &self.handle.buf[self.handle.index]);
1240 const next_index = self.index + darwin_entry.d_reclen;1336 const next_index = self.handle.index + darwin_entry.d_reclen;
1241 self.index = next_index;1337 self.handle.index = next_index;
12421338
1243 const name = @ptrCast([*]u8, &darwin_entry.d_name)[0..darwin_entry.d_namlen];1339 const name = @ptrCast([*]u8, &darwin_entry.d_name)[0..darwin_entry.d_namlen];
12441340
1245 // skip . and .. entries
1246 if (mem.eql(u8, name, ".") or mem.eql(u8, name, "..")) {1341 if (mem.eql(u8, name, ".") or mem.eql(u8, name, "..")) {
1247 continue :start_over;1342 continue :start_over;
1248 }1343 }
...@@ -1266,38 +1361,59 @@ pub const Dir = struct {...@@ -1266,38 +1361,59 @@ pub const Dir = struct {
1266 }1361 }
12671362
1268 fn nextWindows(self: *Dir) !?Entry {1363 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 }
1270 }1386 }
12711387
1272 fn nextLinux(self: *Dir) !?Entry {1388 fn nextLinux(self: *Dir) !?Entry {
1273 start_over: while (true) {1389 start_over: while (true) {
1274 if (self.index >= self.end_index) {1390 if (self.handle.index >= self.handle.end_index) {
1275 if (self.buf.len == 0) {1391 if (self.handle.buf.len == 0) {
1276 self.buf = try self.allocator.alloc(u8, page_size);1392 self.handle.buf = try self.allocator.alloc(u8, page_size);
1277 }1393 }
12781394
1279 while (true) {1395 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);
1281 const err = posix.getErrno(result);1397 const err = posix.getErrno(result);
1282 if (err > 0) {1398 if (err > 0) {
1283 switch (err) {1399 switch (err) {
1284 posix.EBADF, posix.EFAULT, posix.ENOTDIR => unreachable,1400 posix.EBADF, posix.EFAULT, posix.ENOTDIR => unreachable,
1285 posix.EINVAL => {1401 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);
1287 continue;1403 continue;
1288 },1404 },
1289 else => return unexpectedErrorPosix(err),1405 else => return unexpectedErrorPosix(err),
1290 }1406 }
1291 }1407 }
1292 if (result == 0) return null;1408 if (result == 0) return null;
1293 self.index = 0;1409 self.handle.index = 0;
1294 self.end_index = result;1410 self.handle.end_index = result;
1295 break;1411 break;
1296 }1412 }
1297 }1413 }
1298 const linux_entry = @ptrCast(*align(1) posix.dirent, &self.buf[self.index]);1414 const linux_entry = @ptrCast(*align(1) posix.dirent, &self.handle.buf[self.handle.index]);
1299 const next_index = self.index + linux_entry.d_reclen;1415 const next_index = self.handle.index + linux_entry.d_reclen;
1300 self.index = next_index;1416 self.handle.index = next_index;
13011417
1302 const name = cstr.toSlice(@ptrCast([*]u8, &linux_entry.d_name));1418 const name = cstr.toSlice(@ptrCast([*]u8, &linux_entry.d_name));
13031419
...@@ -1306,7 +1422,7 @@ pub const Dir = struct {...@@ -1306,7 +1422,7 @@ pub const Dir = struct {
1306 continue :start_over;1422 continue :start_over;
1307 }1423 }
13081424
1309 const type_char = self.buf[next_index - 1];1425 const type_char = self.handle.buf[next_index - 1];
1310 const entry_kind = switch (type_char) {1426 const entry_kind = switch (type_char) {
1311 posix.DT_BLK => Entry.Kind.BlockDevice,1427 posix.DT_BLK => Entry.Kind.BlockDevice,
1312 posix.DT_CHR => Entry.Kind.CharacterDevice,1428 posix.DT_CHR => Entry.Kind.CharacterDevice,
std/os/test.zig-9
...@@ -10,11 +10,6 @@ const AtomicRmwOp = builtin.AtomicRmwOp;...@@ -10,11 +10,6 @@ const AtomicRmwOp = builtin.AtomicRmwOp;
10const AtomicOrder = builtin.AtomicOrder;10const AtomicOrder = builtin.AtomicOrder;
1111
12test "makePath, put some files in it, deleteTree" {12test "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 }
18 try os.makePath(a, "os_test_tmp/b/c");13 try os.makePath(a, "os_test_tmp/b/c");
19 try io.writeFile(a, "os_test_tmp/b/c/file.txt", "nonsense");14 try io.writeFile(a, "os_test_tmp/b/c/file.txt", "nonsense");
20 try io.writeFile(a, "os_test_tmp/b/file2.txt", "blah");15 try io.writeFile(a, "os_test_tmp/b/file2.txt", "blah");
...@@ -27,10 +22,6 @@ test "makePath, put some files in it, deleteTree" {...@@ -27,10 +22,6 @@ test "makePath, put some files in it, deleteTree" {
27}22}
2823
29test "access file" {24test "access file" {
30 if (builtin.os == builtin.Os.windows) {
31 return;
32 }
33
34 try os.makePath(a, "os_test_tmp");25 try os.makePath(a, "os_test_tmp");
35 if (os.File.access(a, "os_test_tmp/file.txt", os.default_file_mode)) |ok| {26 if (os.File.access(a, "os_test_tmp/file.txt", os.default_file_mode)) |ok| {
36 unreachable;27 unreachable;
std/os/time.zig+4-2
...@@ -68,11 +68,13 @@ pub const milliTimestamp = switch (builtin.os) {...@@ -68,11 +68,13 @@ pub const milliTimestamp = switch (builtin.os) {
68fn milliTimestampWindows() u64 {68fn milliTimestampWindows() u64 {
69 //FileTime has a granularity of 100 nanoseconds69 //FileTime has a granularity of 100 nanoseconds
70 // and uses the NTFS/Windows epoch70 // and uses the NTFS/Windows epoch
71 var ft: i64 = undefined;71 var ft: windows.FILETIME = undefined;
72 windows.GetSystemTimeAsFileTime(&ft);72 windows.GetSystemTimeAsFileTime(&ft);
73 const hns_per_ms = (ns_per_s / 100) / ms_per_s;73 const hns_per_ms = (ns_per_s / 100) / ms_per_s;
74 const epoch_adj = epoch.windows * ms_per_s;74 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;
76}78}
7779
78fn milliTimestampDarwin() u64 {80fn milliTimestampDarwin() u64 {
std/os/windows/index.zig+43-5
...@@ -1,3 +1,7 @@...@@ -1,3 +1,7 @@
1test "import" {
2 _ = @import("util.zig");
3}
4
1pub const ERROR = @import("error.zig");5pub const ERROR = @import("error.zig");
26
3pub extern "advapi32" stdcallcc fn CryptAcquireContextA(7pub extern "advapi32" stdcallcc fn CryptAcquireContextA(
...@@ -61,6 +65,10 @@ pub extern "kernel32" stdcallcc fn DeleteFileA(lpFileName: LPCSTR) BOOL;...@@ -61,6 +65,10 @@ pub extern "kernel32" stdcallcc fn DeleteFileA(lpFileName: LPCSTR) BOOL;
6165
62pub extern "kernel32" stdcallcc fn ExitProcess(exit_code: UINT) noreturn;66pub 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
64pub extern "kernel32" stdcallcc fn FreeEnvironmentStringsA(penv: [*]u8) BOOL;72pub extern "kernel32" stdcallcc fn FreeEnvironmentStringsA(penv: [*]u8) BOOL;
6573
66pub extern "kernel32" stdcallcc fn GetCommandLineA() LPSTR;74pub extern "kernel32" stdcallcc fn GetCommandLineA() LPSTR;
...@@ -77,6 +85,8 @@ pub extern "kernel32" stdcallcc fn GetExitCodeProcess(hProcess: HANDLE, lpExitCo...@@ -77,6 +85,8 @@ pub extern "kernel32" stdcallcc fn GetExitCodeProcess(hProcess: HANDLE, lpExitCo
7785
78pub extern "kernel32" stdcallcc fn GetFileSizeEx(hFile: HANDLE, lpFileSize: *LARGE_INTEGER) BOOL;86pub extern "kernel32" stdcallcc fn GetFileSizeEx(hFile: HANDLE, lpFileSize: *LARGE_INTEGER) BOOL;
7987
88pub extern "kernel32" stdcallcc fn GetFileAttributesA(lpFileName: LPCSTR) DWORD;
89
80pub extern "kernel32" stdcallcc fn GetModuleFileNameA(hModule: ?HMODULE, lpFilename: LPSTR, nSize: DWORD) DWORD;90pub extern "kernel32" stdcallcc fn GetModuleFileNameA(hModule: ?HMODULE, lpFilename: LPSTR, nSize: DWORD) DWORD;
8191
82pub extern "kernel32" stdcallcc fn GetLastError() DWORD;92pub extern "kernel32" stdcallcc fn GetLastError() DWORD;
...@@ -97,7 +107,7 @@ pub extern "kernel32" stdcallcc fn GetFinalPathNameByHandleA(...@@ -97,7 +107,7 @@ pub extern "kernel32" stdcallcc fn GetFinalPathNameByHandleA(
97107
98pub extern "kernel32" stdcallcc fn GetProcessHeap() ?HANDLE;108pub extern "kernel32" stdcallcc fn GetProcessHeap() ?HANDLE;
99109
100pub extern "kernel32" stdcallcc fn GetSystemTimeAsFileTime(?*FILETIME) void;110pub extern "kernel32" stdcallcc fn GetSystemTimeAsFileTime(*FILETIME) void;
101111
102pub extern "kernel32" stdcallcc fn HeapCreate(flOptions: DWORD, dwInitialSize: SIZE_T, dwMaximumSize: SIZE_T) ?HANDLE;112pub extern "kernel32" stdcallcc fn HeapCreate(flOptions: DWORD, dwInitialSize: SIZE_T, dwMaximumSize: SIZE_T) ?HANDLE;
103pub extern "kernel32" stdcallcc fn HeapDestroy(hHeap: HANDLE) BOOL;113pub extern "kernel32" stdcallcc fn HeapDestroy(hHeap: HANDLE) BOOL;
...@@ -131,6 +141,8 @@ pub extern "kernel32" stdcallcc fn ReadFile(...@@ -131,6 +141,8 @@ pub extern "kernel32" stdcallcc fn ReadFile(
131 in_out_lpOverlapped: ?*OVERLAPPED,141 in_out_lpOverlapped: ?*OVERLAPPED,
132) BOOL;142) BOOL;
133143
144pub extern "kernel32" stdcallcc fn RemoveDirectoryA(lpPathName: LPCSTR) BOOL;
145
134pub extern "kernel32" stdcallcc fn SetFilePointerEx(146pub extern "kernel32" stdcallcc fn SetFilePointerEx(
135 in_fFile: HANDLE,147 in_fFile: HANDLE,
136 in_liDistanceToMove: LARGE_INTEGER,148 in_liDistanceToMove: LARGE_INTEGER,
...@@ -196,7 +208,6 @@ pub const UNICODE = false;...@@ -196,7 +208,6 @@ pub const UNICODE = false;
196pub const WCHAR = u16;208pub const WCHAR = u16;
197pub const WORD = u16;209pub const WORD = u16;
198pub const LARGE_INTEGER = i64;210pub const LARGE_INTEGER = i64;
199pub const FILETIME = i64;
200211
201pub const TRUE = 1;212pub const TRUE = 1;
202pub const FALSE = 0;213pub const FALSE = 0;
...@@ -212,6 +223,8 @@ pub const STD_ERROR_HANDLE = @maxValue(DWORD) - 12 + 1;...@@ -212,6 +223,8 @@ pub const STD_ERROR_HANDLE = @maxValue(DWORD) - 12 + 1;
212223
213pub const INVALID_HANDLE_VALUE = @intToPtr(HANDLE, @maxValue(usize));224pub const INVALID_HANDLE_VALUE = @intToPtr(HANDLE, @maxValue(usize));
214225
226pub const INVALID_FILE_ATTRIBUTES = DWORD(@maxValue(DWORD));
227
215pub const OVERLAPPED = extern struct {228pub const OVERLAPPED = extern struct {
216 Internal: ULONG_PTR,229 Internal: ULONG_PTR,
217 InternalHigh: ULONG_PTR,230 InternalHigh: ULONG_PTR,
...@@ -293,13 +306,24 @@ pub const OPEN_EXISTING = 3;...@@ -293,13 +306,24 @@ pub const OPEN_EXISTING = 3;
293pub const TRUNCATE_EXISTING = 5;306pub const TRUNCATE_EXISTING = 5;
294307
295pub const FILE_ATTRIBUTE_ARCHIVE = 0x20;308pub const FILE_ATTRIBUTE_ARCHIVE = 0x20;
309pub const FILE_ATTRIBUTE_COMPRESSED = 0x800;
310pub const FILE_ATTRIBUTE_DEVICE = 0x40;
311pub const FILE_ATTRIBUTE_DIRECTORY = 0x10;
296pub const FILE_ATTRIBUTE_ENCRYPTED = 0x4000;312pub const FILE_ATTRIBUTE_ENCRYPTED = 0x4000;
297pub const FILE_ATTRIBUTE_HIDDEN = 0x2;313pub const FILE_ATTRIBUTE_HIDDEN = 0x2;
314pub const FILE_ATTRIBUTE_INTEGRITY_STREAM = 0x8000;
298pub const FILE_ATTRIBUTE_NORMAL = 0x80;315pub const FILE_ATTRIBUTE_NORMAL = 0x80;
316pub const FILE_ATTRIBUTE_NOT_CONTENT_INDEXED = 0x2000;
317pub const FILE_ATTRIBUTE_NO_SCRUB_DATA = 0x20000;
299pub const FILE_ATTRIBUTE_OFFLINE = 0x1000;318pub const FILE_ATTRIBUTE_OFFLINE = 0x1000;
300pub const FILE_ATTRIBUTE_READONLY = 0x1;319pub 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;
301pub const FILE_ATTRIBUTE_SYSTEM = 0x4;324pub const FILE_ATTRIBUTE_SYSTEM = 0x4;
302pub const FILE_ATTRIBUTE_TEMPORARY = 0x100;325pub const FILE_ATTRIBUTE_TEMPORARY = 0x100;
326pub const FILE_ATTRIBUTE_VIRTUAL = 0x10000;
303327
304pub const PROCESS_INFORMATION = extern struct {328pub const PROCESS_INFORMATION = extern struct {
305 hProcess: HANDLE,329 hProcess: HANDLE,
...@@ -372,6 +396,20 @@ pub const HEAP_NO_SERIALIZE = 0x00000001;...@@ -372,6 +396,20 @@ pub const HEAP_NO_SERIALIZE = 0x00000001;
372pub const PTHREAD_START_ROUTINE = extern fn (LPVOID) DWORD;396pub const PTHREAD_START_ROUTINE = extern fn (LPVOID) DWORD;
373pub const LPTHREAD_START_ROUTINE = PTHREAD_START_ROUTINE;397pub const LPTHREAD_START_ROUTINE = PTHREAD_START_ROUTINE;
374398
375test "import" {399pub const WIN32_FIND_DATAA = extern struct {
376 _ = @import("util.zig");400 dwFileAttributes: DWORD,
377}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" {...@@ -170,3 +170,41 @@ test "InvalidDll" {
170 return;170 return;
171 };171 };
172}172}
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}