feat: allow walk_tree to follow symlinks

This commit is contained in:
CJ van den Berg 2026-02-06 13:49:51 +01:00
parent 0e0137677f
commit 3a718bf6f6
Signed by: neurocyte
GPG key ID: 8EB1E1BB660E3FB9

View file

@ -169,23 +169,14 @@ const FilteredWalker = struct {
try self.name_buffer.appendSlice(self.allocator, base.name);
switch (base.kind) {
.directory => {
if (is_filtered_dir(base.name))
continue;
var new_dir = top.iter.dir.openDir(base.name, .{ .iterate = true }) catch |err| switch (err) {
error.NameTooLong => @panic("unexpected error.NameTooLong"), // no path sep in base.name
else => continue,
};
{
errdefer new_dir.close();
try self.stack.append(self.allocator, .{
.iter = new_dir.iterateAssumeFirstIteration(),
.dirname_len = self.name_buffer.items.len,
});
top = &self.stack.items[self.stack.items.len - 1];
containing = &self.stack.items[self.stack.items.len - 2];
}
_ = try self.next_directory(&base, &top, &containing);
continue;
},
.file => return self.name_buffer.items,
.sym_link => if (try self.next_sym_link(&base, &top, &containing, 5)) |file|
return file
else
continue,
else => continue,
}
} else {
@ -198,4 +189,37 @@ const FilteredWalker = struct {
}
return null;
}
fn next_directory(self: *FilteredWalker, base: *const std.fs.Dir.Entry, top: **StackItem, containing: **StackItem) !void {
if (is_filtered_dir(base.name))
return;
var new_dir = top.*.iter.dir.openDir(base.name, .{ .iterate = true }) catch |err| switch (err) {
error.NameTooLong => @panic("unexpected error.NameTooLong"), // no path sep in base.name
else => return,
};
{
errdefer new_dir.close();
try self.stack.append(self.allocator, .{
.iter = new_dir.iterateAssumeFirstIteration(),
.dirname_len = self.name_buffer.items.len,
});
top.* = &self.stack.items[self.stack.items.len - 1];
containing.* = &self.stack.items[self.stack.items.len - 2];
}
return;
}
fn next_sym_link(self: *FilteredWalker, base: *const std.fs.Dir.Entry, top: **StackItem, containing: **StackItem, stat_depth: usize) !?[]const u8 {
if (stat_depth == 0) return null;
const st = top.*.iter.dir.statFile(base.name) catch return null;
switch (st.kind) {
.directory => {
_ = try self.next_directory(base, top, containing);
return null;
},
.file => return self.name_buffer.items,
.sym_link => return try self.next_sym_link(base, top, containing, stat_depth - 1),
else => return null,
}
}
};