diff --git a/std/c/freebsd.zig b/std/c/freebsd.zig index a97cf52eb9..fbcd6236f0 100644 --- a/std/c/freebsd.zig +++ b/std/c/freebsd.zig @@ -13,6 +13,7 @@ pub extern "c" fn kevent( pub extern "c" fn sysctl(name: [*]c_int, namelen: c_uint, oldp: ?*c_void, oldlenp: ?*usize, newp: ?*c_void, newlen: usize) c_int; pub extern "c" fn sysctlbyname(name: [*]const u8, oldp: ?*c_void, oldlenp: ?*usize, newp: ?*c_void, newlen: usize) c_int; pub extern "c" fn sysctlnametomib(name: [*]const u8, mibp: ?*c_int, sizep: ?*usize) c_int; +pub extern "c" fn getdirentries(fd: c_int, buf_ptr: [*]u8, nbytes: usize, basep: *i64) usize; /// Renamed from `kevent` to `Kevent` to avoid conflict with function name. pub const Kevent = extern struct { @@ -66,3 +67,14 @@ pub const timespec = extern struct { tv_sec: isize, tv_nsec: isize, }; + +pub const dirent = extern struct { + d_fileno: usize, + d_off: i64, + d_reclen: u64, + d_type: u8, + d_pad0: u8, + d_namlen: u16, + d_pad1: u16, + d_name: [256]u8, +}; diff --git a/std/os/freebsd/index.zig b/std/os/freebsd/index.zig index 975479c1e0..880a49b4a8 100644 --- a/std/os/freebsd/index.zig +++ b/std/os/freebsd/index.zig @@ -562,6 +562,10 @@ pub fn getdents(fd: i32, dirp: [*]u8, count: usize) usize { return arch.syscall3(SYS_getdents, @bitCast(usize, isize(fd)), @ptrToInt(dirp), count); } +pub fn getdirentries(fd: i32, buf_ptr: [*]u8, buf_len: usize, basep: *i64) usize { + return errnoWrap(@bitCast(isize, c.getdirentries(fd, buf_ptr, buf_len, basep))); +} + pub fn isatty(fd: i32) bool { var wsz: winsize = undefined; return arch.syscall3(SYS_ioctl, @bitCast(usize, isize(fd)), TIOCGWINSZ, @ptrToInt(&wsz)) == 0; @@ -743,6 +747,7 @@ pub fn raise(sig: i32) usize { } pub const Stat = c.Stat; +pub const dirent = c.dirent; pub const timespec = c.timespec; pub fn fstat(fd: i32, stat_buf: *Stat) usize { diff --git a/std/os/index.zig b/std/os/index.zig index be82ad4716..778ab156b0 100644 --- a/std/os/index.zig +++ b/std/os/index.zig @@ -1753,8 +1753,57 @@ pub const Dir = struct { } fn nextFreebsd(self: *Dir) !?Entry { - //self.handle.buf = try self.allocator.alloc(u8, page_size); - @compileError("TODO implement dirs for FreeBSD"); + start_over: while (true) { + if (self.handle.index >= self.handle.end_index) { + if (self.handle.buf.len == 0) { + self.handle.buf = try self.allocator.alloc(u8, page_size); + } + + while (true) { + const result = posix.getdirentries(self.handle.fd, self.handle.buf.ptr, self.handle.buf.len, &self.handle.seek); + const err = posix.getErrno(result); + if (err > 0) { + switch (err) { + posix.EBADF, posix.EFAULT, posix.ENOTDIR => unreachable, + posix.EINVAL => { + self.handle.buf = try self.allocator.realloc(u8, self.handle.buf, self.handle.buf.len * 2); + continue; + }, + else => return unexpectedErrorPosix(err), + } + } + if (result == 0) return null; + self.handle.index = 0; + self.handle.end_index = result; + break; + } + } + const freebsd_entry = @ptrCast(*align(1) posix.dirent, &self.handle.buf[self.handle.index]); + const next_index = self.handle.index + freebsd_entry.d_reclen; + self.handle.index = next_index; + + const name = @ptrCast([*]u8, &freebsd_entry.d_name)[0..freebsd_entry.d_namlen]; + + if (mem.eql(u8, name, ".") or mem.eql(u8, name, "..")) { + continue :start_over; + } + + const entry_kind = switch (freebsd_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.Whiteout, + else => Entry.Kind.Unknown, + }; + return Entry{ + .name = name, + .kind = entry_kind, + }; + } } };