Skip to content

Commit 3dd9af9

Browse files
committedJun 12, 2018
implement std.os.Dir for windows
improve std.os.File.access so that it does not depend on shlwapi.dll closes #1084
·
0.15.20.3.0
1 parent 0a18d53 commit 3dd9af9

File tree

7 files changed

+293
-102
lines changed

7 files changed

+293
-102
lines changed
 

‎doc/docgen.zig‎

Lines changed: 2 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -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‎

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -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‎

Lines changed: 191 additions & 75 deletions
Original file line numberDiff line numberDiff line change
@@ -734,7 +734,23 @@ pub fn atomicSymLink(allocator: *Allocator, existing_path: []const u8, new_path:
734734
}
735735
}
736736

737-
pub fn deleteFile(allocator: *Allocator, file_path: []const u8) !void {
737+
pub 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+
753+
pub 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

1038+
pub 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
1024-
pub fn deleteDir(allocator: *Allocator, dir_path: []const u8) !void {
1055+
pub 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
10531099
const 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

11411187
pub 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‎

Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -10,11 +10,6 @@ const AtomicRmwOp = builtin.AtomicRmwOp;
1010
const AtomicOrder = builtin.AtomicOrder;
1111

1212
test "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

2924
test "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‎

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -68,11 +68,13 @@ pub const milliTimestamp = switch (builtin.os) {
6868
fn 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

7880
fn milliTimestampDarwin() u64 {

‎std/os/windows/index.zig‎

Lines changed: 43 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
1+
test "import" {
2+
_ = @import("util.zig");
3+
}
4+
15
pub const ERROR = @import("error.zig");
26

37
pub extern "advapi32" stdcallcc fn CryptAcquireContextA(
@@ -61,6 +65,10 @@ pub extern "kernel32" stdcallcc fn DeleteFileA(lpFileName: LPCSTR) BOOL;
6165

6266
pub extern "kernel32" stdcallcc fn ExitProcess(exit_code: UINT) noreturn;
6367

68+
pub extern "kernel32" stdcallcc fn FindFirstFileA(lpFileName: LPCSTR, lpFindFileData: *WIN32_FIND_DATAA) HANDLE;
69+
pub extern "kernel32" stdcallcc fn FindClose(hFindFile: HANDLE) BOOL;
70+
pub extern "kernel32" stdcallcc fn FindNextFileA(hFindFile: HANDLE, lpFindFileData: *WIN32_FIND_DATAA) BOOL;
71+
6472
pub extern "kernel32" stdcallcc fn FreeEnvironmentStringsA(penv: [*]u8) BOOL;
6573

6674
pub extern "kernel32" stdcallcc fn GetCommandLineA() LPSTR;
@@ -77,6 +85,8 @@ pub extern "kernel32" stdcallcc fn GetExitCodeProcess(hProcess: HANDLE, lpExitCo
7785

7886
pub extern "kernel32" stdcallcc fn GetFileSizeEx(hFile: HANDLE, lpFileSize: *LARGE_INTEGER) BOOL;
7987

88+
pub extern "kernel32" stdcallcc fn GetFileAttributesA(lpFileName: LPCSTR) DWORD;
89+
8090
pub extern "kernel32" stdcallcc fn GetModuleFileNameA(hModule: ?HMODULE, lpFilename: LPSTR, nSize: DWORD) DWORD;
8191

8292
pub extern "kernel32" stdcallcc fn GetLastError() DWORD;
@@ -97,7 +107,7 @@ pub extern "kernel32" stdcallcc fn GetFinalPathNameByHandleA(
97107

98108
pub extern "kernel32" stdcallcc fn GetProcessHeap() ?HANDLE;
99109

100-
pub extern "kernel32" stdcallcc fn GetSystemTimeAsFileTime(?*FILETIME) void;
110+
pub extern "kernel32" stdcallcc fn GetSystemTimeAsFileTime(*FILETIME) void;
101111

102112
pub extern "kernel32" stdcallcc fn HeapCreate(flOptions: DWORD, dwInitialSize: SIZE_T, dwMaximumSize: SIZE_T) ?HANDLE;
103113
pub 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

144+
pub extern "kernel32" stdcallcc fn RemoveDirectoryA(lpPathName: LPCSTR) BOOL;
145+
134146
pub extern "kernel32" stdcallcc fn SetFilePointerEx(
135147
in_fFile: HANDLE,
136148
in_liDistanceToMove: LARGE_INTEGER,
@@ -196,7 +208,6 @@ pub const UNICODE = false;
196208
pub const WCHAR = u16;
197209
pub const WORD = u16;
198210
pub const LARGE_INTEGER = i64;
199-
pub const FILETIME = i64;
200211

201212
pub const TRUE = 1;
202213
pub const FALSE = 0;
@@ -212,6 +223,8 @@ pub const STD_ERROR_HANDLE = @maxValue(DWORD) - 12 + 1;
212223

213224
pub const INVALID_HANDLE_VALUE = @intToPtr(HANDLE, @maxValue(usize));
214225

226+
pub const INVALID_FILE_ATTRIBUTES = DWORD(@maxValue(DWORD));
227+
215228
pub const OVERLAPPED = extern struct {
216229
Internal: ULONG_PTR,
217230
InternalHigh: ULONG_PTR,
@@ -293,13 +306,24 @@ pub const OPEN_EXISTING = 3;
293306
pub const TRUNCATE_EXISTING = 5;
294307

295308
pub const FILE_ATTRIBUTE_ARCHIVE = 0x20;
309+
pub const FILE_ATTRIBUTE_COMPRESSED = 0x800;
310+
pub const FILE_ATTRIBUTE_DEVICE = 0x40;
311+
pub const FILE_ATTRIBUTE_DIRECTORY = 0x10;
296312
pub const FILE_ATTRIBUTE_ENCRYPTED = 0x4000;
297313
pub const FILE_ATTRIBUTE_HIDDEN = 0x2;
314+
pub const FILE_ATTRIBUTE_INTEGRITY_STREAM = 0x8000;
298315
pub const FILE_ATTRIBUTE_NORMAL = 0x80;
316+
pub const FILE_ATTRIBUTE_NOT_CONTENT_INDEXED = 0x2000;
317+
pub const FILE_ATTRIBUTE_NO_SCRUB_DATA = 0x20000;
299318
pub const FILE_ATTRIBUTE_OFFLINE = 0x1000;
300319
pub const FILE_ATTRIBUTE_READONLY = 0x1;
320+
pub const FILE_ATTRIBUTE_RECALL_ON_DATA_ACCESS = 0x400000;
321+
pub const FILE_ATTRIBUTE_RECALL_ON_OPEN = 0x40000;
322+
pub const FILE_ATTRIBUTE_REPARSE_POINT = 0x400;
323+
pub const FILE_ATTRIBUTE_SPARSE_FILE = 0x200;
301324
pub const FILE_ATTRIBUTE_SYSTEM = 0x4;
302325
pub const FILE_ATTRIBUTE_TEMPORARY = 0x100;
326+
pub const FILE_ATTRIBUTE_VIRTUAL = 0x10000;
303327

304328
pub const PROCESS_INFORMATION = extern struct {
305329
hProcess: HANDLE,
@@ -372,6 +396,20 @@ pub const HEAP_NO_SERIALIZE = 0x00000001;
372396
pub const PTHREAD_START_ROUTINE = extern fn (LPVOID) DWORD;
373397
pub const LPTHREAD_START_ROUTINE = PTHREAD_START_ROUTINE;
374398

375-
test "import" {
376-
_ = @import("util.zig");
377-
}
399+
pub 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+
412+
pub const FILETIME = extern struct {
413+
dwLowDateTime: DWORD,
414+
dwHighDateTime: DWORD,
415+
};

‎std/os/windows/util.zig‎

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -170,3 +170,41 @@ test "InvalidDll" {
170170
return;
171171
};
172172
}
173+
174+
175+
pub 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.
201+
pub 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+
}

0 commit comments

Comments
 (0)
Please sign in to comment.