diff --git a/std/c/darwin.zig b/std/c/darwin.zig index f0890d4ec0..aa49dfa3df 100644 --- a/std/c/darwin.zig +++ b/std/c/darwin.zig @@ -1,6 +1,7 @@ extern "c" fn __error() &c_int; pub extern "c" fn _NSGetExecutablePath(buf: &u8, bufsize: &u32) c_int; +pub extern "c" fn __getdirentries64(fd: c_int, buf_ptr: &u8, buf_len: usize, basep: &i64) usize; pub use @import("../os/darwin_errno.zig"); @@ -45,3 +46,12 @@ pub const Sigaction = extern struct { sa_mask: sigset_t, sa_flags: c_int, }; + +pub const dirent = extern struct { + d_ino: usize, + d_seekoff: usize, + d_reclen: u16, + d_namlen: u16, + d_type: u8, + d_name: u8, // field address is address of first byte of name +}; diff --git a/std/c/index.zig b/std/c/index.zig index ce9f3c473a..369ea2b358 100644 --- a/std/c/index.zig +++ b/std/c/index.zig @@ -44,6 +44,7 @@ pub extern "c" fn sigaction(sig: c_int, noalias act: &const Sigaction, noalias o pub extern "c" fn nanosleep(rqtp: &const timespec, rmtp: ?×pec) c_int; pub extern "c" fn setreuid(ruid: c_uint, euid: c_uint) c_int; pub extern "c" fn setregid(rgid: c_uint, egid: c_uint) c_int; +pub extern "c" fn rmdir(path: &const u8) c_int; pub extern "c" fn aligned_alloc(alignment: usize, size: usize) ?&c_void; pub extern "c" fn malloc(usize) ?&c_void; diff --git a/std/os/darwin.zig b/std/os/darwin.zig index ebc6f65f55..f8b1fbed3b 100644 --- a/std/os/darwin.zig +++ b/std/os/darwin.zig @@ -56,10 +56,32 @@ pub const O_SYMLINK = 0x200000; /// allow open of symlinks pub const O_EVTONLY = 0x8000; /// descriptor requested for event notifications only pub const O_CLOEXEC = 0x1000000; /// mark as close-on-exec +pub const O_ACCMODE = 3; +pub const O_ALERT = 536870912; +pub const O_ASYNC = 64; +pub const O_DIRECTORY = 1048576; +pub const O_DP_GETRAWENCRYPTED = 1; +pub const O_DP_GETRAWUNENCRYPTED = 2; +pub const O_DSYNC = 4194304; +pub const O_FSYNC = O_SYNC; +pub const O_NOCTTY = 131072; +pub const O_POPUP = 2147483648; +pub const O_SYNC = 128; + pub const SEEK_SET = 0x0; pub const SEEK_CUR = 0x1; pub const SEEK_END = 0x2; +pub const DT_UNKNOWN = 0; +pub const DT_FIFO = 1; +pub const DT_CHR = 2; +pub const DT_DIR = 4; +pub const DT_BLK = 6; +pub const DT_REG = 8; +pub const DT_LNK = 10; +pub const DT_SOCK = 12; +pub const DT_WHT = 14; + pub const SIG_BLOCK = 1; /// block specified signal set pub const SIG_UNBLOCK = 2; /// unblock specified signal set pub const SIG_SETMASK = 3; /// set specified signal set @@ -192,6 +214,11 @@ pub fn pipe(fds: &[2]i32) usize { return errnoWrap(c.pipe(@ptrCast(&c_int, fds))); } + +pub fn getdirentries64(fd: i32, buf_ptr: &u8, buf_len: usize, basep: &i64) usize { + return errnoWrap(@bitCast(isize, c.__getdirentries64(fd, buf_ptr, buf_len, basep))); +} + pub fn mkdir(path: &const u8, mode: u32) usize { return errnoWrap(c.mkdir(path, mode)); } @@ -204,6 +231,10 @@ pub fn rename(old: &const u8, new: &const u8) usize { return errnoWrap(c.rename(old, new)); } +pub fn rmdir(path: &const u8) usize { + return errnoWrap(c.rmdir(path)); +} + pub fn chdir(path: &const u8) usize { return errnoWrap(c.chdir(path)); } @@ -268,6 +299,7 @@ pub const empty_sigset = sigset_t(0); pub const timespec = c.timespec; pub const Stat = c.Stat; +pub const dirent = c.dirent; /// Renamed from `sigaction` to `Sigaction` to avoid conflict with the syscall. pub const Sigaction = struct { diff --git a/std/os/index.zig b/std/os/index.zig index 09409d6d36..eb753db9b7 100644 --- a/std/os/index.zig +++ b/std/os/index.zig @@ -1055,10 +1055,11 @@ pub fn deleteTree(allocator: &Allocator, full_path: []const u8) DeleteTreeError! return; } else |err| switch (err) { error.FileNotFound => return, + + error.AccessDenied, error.IsDir => {}, error.OutOfMemory, - error.AccessDenied, error.SymLinkLoop, error.NameTooLong, error.SystemResources, @@ -1109,18 +1110,16 @@ pub fn deleteTree(allocator: &Allocator, full_path: []const u8) DeleteTreeError! } pub const Dir = struct { - // See man getdents fd: i32, + darwin_seek: darwin_seek_t, allocator: &Allocator, buf: []u8, index: usize, end_index: usize, - const LinuxEntry = extern struct { - d_ino: usize, - d_off: usize, - d_reclen: u16, - d_name: u8, // field address is the address of first byte of name + const darwin_seek_t = switch (builtin.os) { + Os.macosx, Os.ios => i64, + else => void, }; pub const Entry = struct { @@ -1135,15 +1134,26 @@ pub const Dir = struct { SymLink, File, UnixDomainSocket, + Wht, // TODO wtf is this Unknown, }; }; pub fn open(allocator: &Allocator, dir_path: []const u8) !Dir { - const fd = try posixOpen(allocator, dir_path, posix.O_RDONLY|posix.O_DIRECTORY|posix.O_CLOEXEC, 0); + const fd = switch (builtin.os) { + Os.windows => @compileError("TODO support Dir.open for windows"), + Os.linux => try posixOpen(allocator, dir_path, posix.O_RDONLY|posix.O_DIRECTORY|posix.O_CLOEXEC, 0), + Os.macosx, Os.ios => try posixOpen(allocator, dir_path, posix.O_RDONLY|posix.O_NONBLOCK|posix.O_DIRECTORY|posix.O_CLOEXEC, 0), + else => @compileError("Dir.open is not supported for this platform"), + }; + const darwin_seek_init = switch (builtin.os) { + Os.macosx, Os.ios => 0, + else => {}, + }; return Dir { .allocator = allocator, .fd = fd, + .darwin_seek = darwin_seek_init, .index = 0, .end_index = 0, .buf = []u8{}, @@ -1158,6 +1168,15 @@ pub const Dir = struct { /// Memory such as file names referenced in this returned entry becomes invalid /// with subsequent calls to next, as well as when this ::Dir is deinitialized. pub fn next(self: &Dir) !?Entry { + switch (builtin.os) { + Os.linux => return self.nextLinux(), + Os.macosx, Os.ios => return self.nextDarwin(), + Os.windows => return self.nextWindows(), + else => @compileError("Dir.next not supported on " ++ @tagName(builtin.os)), + } + } + + fn nextDarwin(self: &Dir) !?Entry { start_over: while (true) { if (self.index >= self.end_index) { if (self.buf.len == 0) { @@ -1165,8 +1184,9 @@ pub const Dir = struct { } while (true) { - const result = posix.getdents(self.fd, self.buf.ptr, self.buf.len); - const err = linux.getErrno(result); + const result = posix.getdirentries64(self.fd, self.buf.ptr, self.buf.len, + &self.darwin_seek); + const err = posix.getErrno(result); if (err > 0) { switch (err) { posix.EBADF, posix.EFAULT, posix.ENOTDIR => unreachable, @@ -1184,7 +1204,67 @@ pub const Dir = struct { break; } } - const linux_entry = @ptrCast(& align(1) LinuxEntry, &self.buf[self.index]); + const darwin_entry = @ptrCast(& align(1) posix.dirent, &self.buf[self.index]); + const next_index = self.index + darwin_entry.d_reclen; + self.index = next_index; + + const name = (&darwin_entry.d_name)[0..darwin_entry.d_namlen]; + + // skip . and .. entries + if (mem.eql(u8, name, ".") or mem.eql(u8, name, "..")) { + continue :start_over; + } + + const entry_kind = switch (darwin_entry.d_type) { + posix.DT_BLK => Entry.Kind.BlockDevice, + posix.DT_CHR => Entry.Kind.CharacterDevice, + posix.DT_DIR => Entry.Kind.Directory, + posix.DT_FIFO => Entry.Kind.NamedPipe, + posix.DT_LNK => Entry.Kind.SymLink, + posix.DT_REG => Entry.Kind.File, + posix.DT_SOCK => Entry.Kind.UnixDomainSocket, + posix.DT_WHT => Entry.Kind.Wht, + else => Entry.Kind.Unknown, + }; + return Entry { + .name = name, + .kind = entry_kind, + }; + } + } + + fn nextWindows(self: &Dir) !?Entry { + @compileError("TODO support Dir.next for windows"); + } + + fn nextLinux(self: &Dir) !?Entry { + start_over: while (true) { + if (self.index >= self.end_index) { + if (self.buf.len == 0) { + self.buf = try self.allocator.alloc(u8, page_size); + } + + while (true) { + const result = posix.getdents(self.fd, self.buf.ptr, self.buf.len); + const err = posix.getErrno(result); + if (err > 0) { + switch (err) { + posix.EBADF, posix.EFAULT, posix.ENOTDIR => unreachable, + posix.EINVAL => { + self.buf = try self.allocator.realloc(u8, self.buf, self.buf.len * 2); + continue; + }, + else => return unexpectedErrorPosix(err), + } + } + if (result == 0) + return null; + self.index = 0; + self.end_index = result; + break; + } + } + const linux_entry = @ptrCast(& align(1) posix.dirent, &self.buf[self.index]); const next_index = self.index + linux_entry.d_reclen; self.index = next_index; @@ -1683,7 +1763,7 @@ test "std.os" { // TODO make this a build variable that you can set -const unexpected_error_tracing = false; +const unexpected_error_tracing = true; /// Call this when you made a syscall or something that sets errno /// and you get an unexpected error. diff --git a/std/os/linux/x86_64.zig b/std/os/linux/x86_64.zig index 3a76ca4f87..cfb2231df9 100644 --- a/std/os/linux/x86_64.zig +++ b/std/os/linux/x86_64.zig @@ -488,3 +488,11 @@ pub const timespec = extern struct { tv_sec: isize, tv_nsec: isize, }; + +pub const dirent = extern struct { + d_ino: usize, + d_off: usize, + d_reclen: u16, + d_name: u8, // field address is the address of first byte of name +}; + diff --git a/std/os/test.zig b/std/os/test.zig new file mode 100644 index 0000000000..2606dc1c58 --- /dev/null +++ b/std/os/test.zig @@ -0,0 +1,12 @@ +const std = @import("../index.zig"); +const os = std.os; +const io = std.io; + +const a = std.debug.global_allocator; + +test "makePath, put some files in it, deleteTree" { + try os.makePath(a, "os_test_tmp/b/c"); + try io.writeFile(a, "os_test_tmp/b/c/file.txt", "nonsense"); + try io.writeFile(a, "os_test_tmp/b/file2.txt", "blah"); + try os.deleteTree(a, "os_test_tmp"); +}