| ... | @@ -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 | } |
| 736 | | 736 | |
| 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 { |
| 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 | } |
| 1021 | | 1037 | |
| | 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 | |
| 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 ::deleteTree | 1054 | /// 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 { |
| 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); |
| 1027 | | 1058 | |
| 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; |
| 1030 | | 1061 | |
| 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 | } |
| 1048 | | 1095 | |
| 1049 | /// Whether ::full_path describes a symlink, file, or directory, this function | 1096 | /// 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 | | |
| 1053 | const DeleteTreeError = error{ | 1099 | const 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); |
| 1133 | | 1179 | |
| 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 | } |
| 1140 | | 1186 | |
| 1141 | pub const Dir = struct { | 1187 | pub 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, | | |
| 1148 | | 1190 | |
| 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 | }; |
| 1153 | | 1212 | |
| 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 | }; |
| 1170 | | 1229 | |
| 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 | } |
| 1196 | | 1284 | |
| 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 | } |
| 1201 | | 1297 | |
| 1202 | /// Memory such as file names referenced in this returned entry becomes invalid | 1298 | /// 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 | } |
| 1212 | | 1308 | |
| 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 | } |
| 1219 | | 1315 | |
| 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; |
| 1242 | | 1338 | |
| 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]; |
| 1244 | | 1340 | |
| 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 | } |
| 1267 | | 1362 | |
| 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 | } |
| 1271 | | 1387 | |
| 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 | } |
| 1278 | | 1394 | |
| 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; |
| 1301 | | 1417 | |
| 1302 | const name = cstr.toSlice(@ptrCast([*]u8, &linux_entry.d_name)); | 1418 | const name = cstr.toSlice(@ptrCast([*]u8, &linux_entry.d_name)); |
| 1303 | | 1419 | |
| ... | @@ -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 | } |
| 1308 | | 1424 | |
| 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, |