| ... | @@ -737,6 +737,94 @@ pub const Dir = struct { | ... | @@ -737,6 +737,94 @@ pub const Dir = struct { |
| 737 | } | 737 | } |
| 738 | } | 738 | } |
| 739 | | 739 | |
| | 740 | pub const Walker = struct { |
| | 741 | stack: std.ArrayList(StackItem), |
| | 742 | name_buffer: std.ArrayList(u8), |
| | 743 | |
| | 744 | pub const WalkerEntry = struct { |
| | 745 | /// The containing directory. This can be used to operate directly on `basename` |
| | 746 | /// rather than `path`, avoiding `error.NameTooLong` for deeply nested paths. |
| | 747 | /// The directory remains open until `next` or `deinit` is called. |
| | 748 | dir: Dir, |
| | 749 | basename: []const u8, |
| | 750 | path: []const u8, |
| | 751 | kind: Dir.Entry.Kind, |
| | 752 | }; |
| | 753 | |
| | 754 | const StackItem = struct { |
| | 755 | iter: Dir.Iterator, |
| | 756 | dirname_len: usize, |
| | 757 | }; |
| | 758 | |
| | 759 | /// After each call to this function, and on deinit(), the memory returned |
| | 760 | /// from this function becomes invalid. A copy must be made in order to keep |
| | 761 | /// a reference to the path. |
| | 762 | pub fn next(self: *Walker) !?WalkerEntry { |
| | 763 | while (self.stack.items.len != 0) { |
| | 764 | // `top` becomes invalid after appending to `self.stack` |
| | 765 | var top = &self.stack.items[self.stack.items.len - 1]; |
| | 766 | const dirname_len = top.dirname_len; |
| | 767 | if (try top.iter.next()) |base| { |
| | 768 | self.name_buffer.shrinkRetainingCapacity(dirname_len); |
| | 769 | if (self.name_buffer.items.len != 0) { |
| | 770 | try self.name_buffer.append(path.sep); |
| | 771 | } |
| | 772 | try self.name_buffer.appendSlice(base.name); |
| | 773 | if (base.kind == .Directory) { |
| | 774 | var new_dir = top.iter.dir.openDir(base.name, .{ .iterate = true }) catch |err| switch (err) { |
| | 775 | error.NameTooLong => unreachable, // no path sep in base.name |
| | 776 | else => |e| return e, |
| | 777 | }; |
| | 778 | { |
| | 779 | errdefer new_dir.close(); |
| | 780 | try self.stack.append(StackItem{ |
| | 781 | .iter = new_dir.iterate(), |
| | 782 | .dirname_len = self.name_buffer.items.len, |
| | 783 | }); |
| | 784 | top = &self.stack.items[self.stack.items.len - 1]; |
| | 785 | } |
| | 786 | } |
| | 787 | return WalkerEntry{ |
| | 788 | .dir = top.iter.dir, |
| | 789 | .basename = self.name_buffer.items[dirname_len + 1 ..], |
| | 790 | .path = self.name_buffer.items, |
| | 791 | .kind = base.kind, |
| | 792 | }; |
| | 793 | } else { |
| | 794 | self.stack.pop().iter.dir.close(); |
| | 795 | } |
| | 796 | } |
| | 797 | return null; |
| | 798 | } |
| | 799 | |
| | 800 | pub fn deinit(self: *Walker) void { |
| | 801 | while (self.stack.popOrNull()) |*item| item.iter.dir.close(); |
| | 802 | self.stack.deinit(); |
| | 803 | self.name_buffer.deinit(); |
| | 804 | } |
| | 805 | }; |
| | 806 | |
| | 807 | /// Recursively iterates over a directory. |
| | 808 | /// Must call `Walker.deinit` when done. |
| | 809 | /// The order of returned file system entries is undefined. |
| | 810 | pub fn walk(self: Dir, allocator: *Allocator) !Walker { |
| | 811 | var name_buffer = std.ArrayList(u8).init(allocator); |
| | 812 | errdefer name_buffer.deinit(); |
| | 813 | |
| | 814 | var stack = std.ArrayList(Walker.StackItem).init(allocator); |
| | 815 | errdefer stack.deinit(); |
| | 816 | |
| | 817 | try stack.append(Walker.StackItem{ |
| | 818 | .iter = self.iterate(), |
| | 819 | .dirname_len = 0, |
| | 820 | }); |
| | 821 | |
| | 822 | return Walker{ |
| | 823 | .stack = stack, |
| | 824 | .name_buffer = name_buffer, |
| | 825 | }; |
| | 826 | } |
| | 827 | |
| 740 | pub const OpenError = error{ | 828 | pub const OpenError = error{ |
| 741 | FileNotFound, | 829 | FileNotFound, |
| 742 | NotDir, | 830 | NotDir, |
| ... | @@ -2259,100 +2347,7 @@ pub fn symLinkAbsoluteZ(target_path_c: [*:0]const u8, sym_link_path_c: [*:0]cons | ... | @@ -2259,100 +2347,7 @@ pub fn symLinkAbsoluteZ(target_path_c: [*:0]const u8, sym_link_path_c: [*:0]cons |
| 2259 | pub const symLink = @compileError("deprecated: use Dir.symLink or symLinkAbsolute"); | 2347 | pub const symLink = @compileError("deprecated: use Dir.symLink or symLinkAbsolute"); |
| 2260 | pub const symLinkC = @compileError("deprecated: use Dir.symLinkZ or symLinkAbsoluteZ"); | 2348 | pub const symLinkC = @compileError("deprecated: use Dir.symLinkZ or symLinkAbsoluteZ"); |
| 2261 | | 2349 | |
| 2262 | pub const Walker = struct { | 2350 | pub const walkPath = @compileError("deprecated: use Dir.walk"); |
| 2263 | stack: std.ArrayList(StackItem), | | |
| 2264 | name_buffer: std.ArrayList(u8), | | |
| 2265 | | | |
| 2266 | pub const Entry = struct { | | |
| 2267 | /// The containing directory. This can be used to operate directly on `basename` | | |
| 2268 | /// rather than `path`, avoiding `error.NameTooLong` for deeply nested paths. | | |
| 2269 | /// The directory remains open until `next` or `deinit` is called. | | |
| 2270 | dir: Dir, | | |
| 2271 | /// TODO make this null terminated for API convenience | | |
| 2272 | basename: []const u8, | | |
| 2273 | | | |
| 2274 | path: []const u8, | | |
| 2275 | kind: Dir.Entry.Kind, | | |
| 2276 | }; | | |
| 2277 | | | |
| 2278 | const StackItem = struct { | | |
| 2279 | dir_it: Dir.Iterator, | | |
| 2280 | dirname_len: usize, | | |
| 2281 | }; | | |
| 2282 | | | |
| 2283 | /// After each call to this function, and on deinit(), the memory returned | | |
| 2284 | /// from this function becomes invalid. A copy must be made in order to keep | | |
| 2285 | /// a reference to the path. | | |
| 2286 | pub fn next(self: *Walker) !?Entry { | | |
| 2287 | while (true) { | | |
| 2288 | if (self.stack.items.len == 0) return null; | | |
| 2289 | // `top` becomes invalid after appending to `self.stack`. | | |
| 2290 | var top = &self.stack.items[self.stack.items.len - 1]; | | |
| 2291 | const dirname_len = top.dirname_len; | | |
| 2292 | if (try top.dir_it.next()) |base| { | | |
| 2293 | self.name_buffer.shrinkRetainingCapacity(dirname_len); | | |
| 2294 | try self.name_buffer.append(path.sep); | | |
| 2295 | try self.name_buffer.appendSlice(base.name); | | |
| 2296 | if (base.kind == .Directory) { | | |
| 2297 | var new_dir = top.dir_it.dir.openDir(base.name, .{ .iterate = true }) catch |err| switch (err) { | | |
| 2298 | error.NameTooLong => unreachable, // no path sep in base.name | | |
| 2299 | else => |e| return e, | | |
| 2300 | }; | | |
| 2301 | { | | |
| 2302 | errdefer new_dir.close(); | | |
| 2303 | try self.stack.append(StackItem{ | | |
| 2304 | .dir_it = new_dir.iterate(), | | |
| 2305 | .dirname_len = self.name_buffer.items.len, | | |
| 2306 | }); | | |
| 2307 | top = &self.stack.items[self.stack.items.len - 1]; | | |
| 2308 | } | | |
| 2309 | } | | |
| 2310 | return Entry{ | | |
| 2311 | .dir = top.dir_it.dir, | | |
| 2312 | .basename = self.name_buffer.items[dirname_len + 1 ..], | | |
| 2313 | .path = self.name_buffer.items, | | |
| 2314 | .kind = base.kind, | | |
| 2315 | }; | | |
| 2316 | } else { | | |
| 2317 | self.stack.pop().dir_it.dir.close(); | | |
| 2318 | } | | |
| 2319 | } | | |
| 2320 | } | | |
| 2321 | | | |
| 2322 | pub fn deinit(self: *Walker) void { | | |
| 2323 | while (self.stack.popOrNull()) |*item| item.dir_it.dir.close(); | | |
| 2324 | self.stack.deinit(); | | |
| 2325 | self.name_buffer.deinit(); | | |
| 2326 | } | | |
| 2327 | }; | | |
| 2328 | | | |
| 2329 | /// Recursively iterates over a directory. | | |
| 2330 | /// Must call `Walker.deinit` when done. | | |
| 2331 | /// `dir_path` must not end in a path separator. | | |
| 2332 | /// The order of returned file system entries is undefined. | | |
| 2333 | pub fn walkPath(allocator: *Allocator, dir_path: []const u8) !Walker { | | |
| 2334 | assert(!mem.endsWith(u8, dir_path, path.sep_str)); | | |
| 2335 | | | |
| 2336 | var dir = try cwd().openDir(dir_path, .{ .iterate = true }); | | |
| 2337 | errdefer dir.close(); | | |
| 2338 | | | |
| 2339 | var name_buffer = std.ArrayList(u8).init(allocator); | | |
| 2340 | errdefer name_buffer.deinit(); | | |
| 2341 | | | |
| 2342 | try name_buffer.appendSlice(dir_path); | | |
| 2343 | | | |
| 2344 | var walker = Walker{ | | |
| 2345 | .stack = std.ArrayList(Walker.StackItem).init(allocator), | | |
| 2346 | .name_buffer = name_buffer, | | |
| 2347 | }; | | |
| 2348 | | | |
| 2349 | try walker.stack.append(Walker.StackItem{ | | |
| 2350 | .dir_it = dir.iterate(), | | |
| 2351 | .dirname_len = dir_path.len, | | |
| 2352 | }); | | |
| 2353 | | | |
| 2354 | return walker; | | |
| 2355 | } | | |
| 2356 | | 2351 | |
| 2357 | pub const OpenSelfExeError = error{ | 2352 | pub const OpenSelfExeError = error{ |
| 2358 | SharingViolation, | 2353 | SharingViolation, |