authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2024-04-14 17:59:43-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2024-04-14 17:59:43-07:00
log10106660e92363d9ff95481eb6a6a4a9d73f843f
tree23521a99dee97774fb4f467919c434d4c8622c2b
parente45bdc6bd6e47ec3f7a06dbb48f24e842bf43a0d

std.fs.Dir.Walker: maintain a null byte in path names


1 files changed, 29 insertions(+), 23 deletions(-)

lib/std/fs/Dir.zig+29-23
......@@ -646,16 +646,17 @@ fn iterateImpl(self: Dir, first_iter_start_value: bool) Iterator {
646646}
647647
648648pub const Walker = struct {
649 stack: std.ArrayList(StackItem),
650 name_buffer: std.ArrayList(u8),
649 stack: std.ArrayListUnmanaged(StackItem),
650 name_buffer: std.ArrayListUnmanaged(u8),
651 allocator: Allocator,
651652
652653 pub const WalkerEntry = struct {
653654 /// The containing directory. This can be used to operate directly on `basename`
654655 /// rather than `path`, avoiding `error.NameTooLong` for deeply nested paths.
655656 /// The directory remains open until `next` or `deinit` is called.
656657 dir: Dir,
657 basename: []const u8,
658 path: []const u8,
658 basename: [:0]const u8,
659 path: [:0]const u8,
659660 kind: Dir.Entry.Kind,
660661 };
661662
......@@ -668,6 +669,7 @@ pub const Walker = struct {
668669 /// from this function becomes invalid. A copy must be made in order to keep
669670 /// a reference to the path.
670671 pub fn next(self: *Walker) !?WalkerEntry {
672 const gpa = self.allocator;
671673 while (self.stack.items.len != 0) {
672674 // `top` and `containing` become invalid after appending to `self.stack`
673675 var top = &self.stack.items[self.stack.items.len - 1];
......@@ -686,10 +688,12 @@ pub const Walker = struct {
686688 }) |base| {
687689 self.name_buffer.shrinkRetainingCapacity(dirname_len);
688690 if (self.name_buffer.items.len != 0) {
689 try self.name_buffer.append(fs.path.sep);
691 try self.name_buffer.append(gpa, fs.path.sep);
690692 dirname_len += 1;
691693 }
692 try self.name_buffer.appendSlice(base.name);
694 try self.name_buffer.ensureUnusedCapacity(gpa, base.name.len + 1);
695 self.name_buffer.appendSliceAssumeCapacity(base.name);
696 self.name_buffer.appendAssumeCapacity(0);
693697 if (base.kind == .directory) {
694698 var new_dir = top.iter.dir.openDir(base.name, .{ .iterate = true }) catch |err| switch (err) {
695699 error.NameTooLong => unreachable, // no path sep in base.name
......@@ -697,18 +701,18 @@ pub const Walker = struct {
697701 };
698702 {
699703 errdefer new_dir.close();
700 try self.stack.append(StackItem{
704 try self.stack.append(gpa, .{
701705 .iter = new_dir.iterateAssumeFirstIteration(),
702 .dirname_len = self.name_buffer.items.len,
706 .dirname_len = self.name_buffer.items.len - 1,
703707 });
704708 top = &self.stack.items[self.stack.items.len - 1];
705709 containing = &self.stack.items[self.stack.items.len - 2];
706710 }
707711 }
708 return WalkerEntry{
712 return .{
709713 .dir = containing.iter.dir,
710 .basename = self.name_buffer.items[dirname_len..],
711 .path = self.name_buffer.items,
714 .basename = self.name_buffer.items[dirname_len .. self.name_buffer.items.len - 1 :0],
715 .path = self.name_buffer.items[0 .. self.name_buffer.items.len - 1 :0],
712716 .kind = base.kind,
713717 };
714718 } else {
......@@ -722,37 +726,39 @@ pub const Walker = struct {
722726 }
723727
724728 pub fn deinit(self: *Walker) void {
729 const gpa = self.allocator;
725730 // Close any remaining directories except the initial one (which is always at index 0)
726731 if (self.stack.items.len > 1) {
727732 for (self.stack.items[1..]) |*item| {
728733 item.iter.dir.close();
729734 }
730735 }
731 self.stack.deinit();
732 self.name_buffer.deinit();
736 self.stack.deinit(gpa);
737 self.name_buffer.deinit(gpa);
733738 }
734739};
735740
736741/// Recursively iterates over a directory.
742///
737743/// `self` must have been opened with `OpenDirOptions{.iterate = true}`.
738/// Must call `Walker.deinit` when done.
744///
745/// `Walker.deinit` releases allocated memory and directory handles.
746///
739747/// The order of returned file system entries is undefined.
748///
740749/// `self` will not be closed after walking it.
741pub fn walk(self: Dir, allocator: Allocator) !Walker {
742 var name_buffer = std.ArrayList(u8).init(allocator);
743 errdefer name_buffer.deinit();
744
745 var stack = std.ArrayList(Walker.StackItem).init(allocator);
746 errdefer stack.deinit();
750pub fn walk(self: Dir, allocator: Allocator) Allocator.Error!Walker {
751 var stack: std.ArrayListUnmanaged(Walker.StackItem) = .{};
747752
748 try stack.append(Walker.StackItem{
753 try stack.append(allocator, .{
749754 .iter = self.iterate(),
750755 .dirname_len = 0,
751756 });
752757
753 return Walker{
758 return .{
754759 .stack = stack,
755 .name_buffer = name_buffer,
760 .name_buffer = .{},
761 .allocator = allocator,
756762 };
757763}
758764