authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2024-04-15 00:25:23-07:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2024-04-15 00:25:23-07:00
log64b989358275d00299403457074e55a431c3a0b7
treebeb55dd1555d7311ab417d41f439a4147cf2d3ec
parente45bdc6bd6e47ec3f7a06dbb48f24e842bf43a0d
parent36060a774e74668a849ffd81545f082c81cbdbfe
signaturebadge-check Signed by PGP key B5690EEEBB952194

Merge pull request #19657 from ziglang/fs.Dir.Walker-null

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

1 files changed, 31 insertions(+), 25 deletions(-)

lib/std/fs/Dir.zig+31-25
...@@ -646,16 +646,17 @@ fn iterateImpl(self: Dir, first_iter_start_value: bool) Iterator {...@@ -646,16 +646,17 @@ fn iterateImpl(self: Dir, first_iter_start_value: bool) Iterator {
646}646}
647647
648pub const Walker = struct {648pub const Walker = struct {
649 stack: std.ArrayList(StackItem),649 stack: std.ArrayListUnmanaged(StackItem),
650 name_buffer: std.ArrayList(u8),650 name_buffer: std.ArrayListUnmanaged(u8),
651 allocator: Allocator,
651652
652 pub const WalkerEntry = struct {653 pub const Entry = struct {
653 /// The containing directory. This can be used to operate directly on `basename`654 /// The containing directory. This can be used to operate directly on `basename`
654 /// rather than `path`, avoiding `error.NameTooLong` for deeply nested paths.655 /// rather than `path`, avoiding `error.NameTooLong` for deeply nested paths.
655 /// The directory remains open until `next` or `deinit` is called.656 /// The directory remains open until `next` or `deinit` is called.
656 dir: Dir,657 dir: Dir,
657 basename: []const u8,658 basename: [:0]const u8,
658 path: []const u8,659 path: [:0]const u8,
659 kind: Dir.Entry.Kind,660 kind: Dir.Entry.Kind,
660 };661 };
661662
...@@ -667,7 +668,8 @@ pub const Walker = struct {...@@ -667,7 +668,8 @@ pub const Walker = struct {
667 /// After each call to this function, and on deinit(), the memory returned668 /// After each call to this function, and on deinit(), the memory returned
668 /// from this function becomes invalid. A copy must be made in order to keep669 /// from this function becomes invalid. A copy must be made in order to keep
669 /// a reference to the path.670 /// a reference to the path.
670 pub fn next(self: *Walker) !?WalkerEntry {671 pub fn next(self: *Walker) !?Walker.Entry {
672 const gpa = self.allocator;
671 while (self.stack.items.len != 0) {673 while (self.stack.items.len != 0) {
672 // `top` and `containing` become invalid after appending to `self.stack`674 // `top` and `containing` become invalid after appending to `self.stack`
673 var top = &self.stack.items[self.stack.items.len - 1];675 var top = &self.stack.items[self.stack.items.len - 1];
...@@ -686,10 +688,12 @@ pub const Walker = struct {...@@ -686,10 +688,12 @@ pub const Walker = struct {
686 }) |base| {688 }) |base| {
687 self.name_buffer.shrinkRetainingCapacity(dirname_len);689 self.name_buffer.shrinkRetainingCapacity(dirname_len);
688 if (self.name_buffer.items.len != 0) {690 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);
690 dirname_len += 1;692 dirname_len += 1;
691 }693 }
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);
693 if (base.kind == .directory) {697 if (base.kind == .directory) {
694 var new_dir = top.iter.dir.openDir(base.name, .{ .iterate = true }) catch |err| switch (err) {698 var new_dir = top.iter.dir.openDir(base.name, .{ .iterate = true }) catch |err| switch (err) {
695 error.NameTooLong => unreachable, // no path sep in base.name699 error.NameTooLong => unreachable, // no path sep in base.name
...@@ -697,18 +701,18 @@ pub const Walker = struct {...@@ -697,18 +701,18 @@ pub const Walker = struct {
697 };701 };
698 {702 {
699 errdefer new_dir.close();703 errdefer new_dir.close();
700 try self.stack.append(StackItem{704 try self.stack.append(gpa, .{
701 .iter = new_dir.iterateAssumeFirstIteration(),705 .iter = new_dir.iterateAssumeFirstIteration(),
702 .dirname_len = self.name_buffer.items.len,706 .dirname_len = self.name_buffer.items.len - 1,
703 });707 });
704 top = &self.stack.items[self.stack.items.len - 1];708 top = &self.stack.items[self.stack.items.len - 1];
705 containing = &self.stack.items[self.stack.items.len - 2];709 containing = &self.stack.items[self.stack.items.len - 2];
706 }710 }
707 }711 }
708 return WalkerEntry{712 return .{
709 .dir = containing.iter.dir,713 .dir = containing.iter.dir,
710 .basename = self.name_buffer.items[dirname_len..],714 .basename = self.name_buffer.items[dirname_len .. self.name_buffer.items.len - 1 :0],
711 .path = self.name_buffer.items,715 .path = self.name_buffer.items[0 .. self.name_buffer.items.len - 1 :0],
712 .kind = base.kind,716 .kind = base.kind,
713 };717 };
714 } else {718 } else {
...@@ -722,37 +726,39 @@ pub const Walker = struct {...@@ -722,37 +726,39 @@ pub const Walker = struct {
722 }726 }
723727
724 pub fn deinit(self: *Walker) void {728 pub fn deinit(self: *Walker) void {
729 const gpa = self.allocator;
725 // Close any remaining directories except the initial one (which is always at index 0)730 // Close any remaining directories except the initial one (which is always at index 0)
726 if (self.stack.items.len > 1) {731 if (self.stack.items.len > 1) {
727 for (self.stack.items[1..]) |*item| {732 for (self.stack.items[1..]) |*item| {
728 item.iter.dir.close();733 item.iter.dir.close();
729 }734 }
730 }735 }
731 self.stack.deinit();736 self.stack.deinit(gpa);
732 self.name_buffer.deinit();737 self.name_buffer.deinit(gpa);
733 }738 }
734};739};
735740
736/// Recursively iterates over a directory.741/// Recursively iterates over a directory.
742///
737/// `self` must have been opened with `OpenDirOptions{.iterate = true}`.743/// `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///
739/// The order of returned file system entries is undefined.747/// The order of returned file system entries is undefined.
748///
740/// `self` will not be closed after walking it.749/// `self` will not be closed after walking it.
741pub fn walk(self: Dir, allocator: Allocator) !Walker {750pub fn walk(self: Dir, allocator: Allocator) Allocator.Error!Walker {
742 var name_buffer = std.ArrayList(u8).init(allocator);751 var stack: std.ArrayListUnmanaged(Walker.StackItem) = .{};
743 errdefer name_buffer.deinit();
744
745 var stack = std.ArrayList(Walker.StackItem).init(allocator);
746 errdefer stack.deinit();
747752
748 try stack.append(Walker.StackItem{753 try stack.append(allocator, .{
749 .iter = self.iterate(),754 .iter = self.iterate(),
750 .dirname_len = 0,755 .dirname_len = 0,
751 });756 });
752757
753 return Walker{758 return .{
754 .stack = stack,759 .stack = stack,
755 .name_buffer = name_buffer,760 .name_buffer = .{},
761 .allocator = allocator,
756 };762 };
757}763}
758764