| ... | ... | @@ -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 | 828 | pub const OpenError = error{ |
| 741 | 829 | FileNotFound, |
| 742 | 830 | NotDir, |
| ... | ... | @@ -2259,100 +2347,7 @@ pub fn symLinkAbsoluteZ(target_path_c: [*:0]const u8, sym_link_path_c: [*:0]cons |
| 2259 | 2347 | pub const symLink = @compileError("deprecated: use Dir.symLink or symLinkAbsolute"); |
| 2260 | 2348 | pub const symLinkC = @compileError("deprecated: use Dir.symLinkZ or symLinkAbsoluteZ"); |
| 2261 | 2349 | |
| 2262 | | pub const Walker = struct { |
| 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 | | } |
| 2350 | pub const walkPath = @compileError("deprecated: use Dir.walk"); |
| 2356 | 2351 | |
| 2357 | 2352 | pub const OpenSelfExeError = error{ |
| 2358 | 2353 | SharingViolation, |