@@ -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
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 ,
0 commit comments