Commit 5ea37f6e8c

Marcio Giaxa <i@mgxm.me>
2018-12-18 01:06:28
freebsd: add getdirentries
1 parent 666b153
Changed files (3)
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,
+};
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 {
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,
+            };
+        }
     }
 };