authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2020-06-20 19:46:14-04:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2020-06-20 19:46:14-04:00
log0a9672fb86b84658f8780f57e769be45e41f3034
tree32a49700828436b19b56e1502b81c8d795ec63a1
parentda549a72e19d4f24520ca151bbd4cd0e74dc752c

rework zig fmt to avoid unnecessary realpath() calls

* add `std.fs.Dir.stat` * zig fmt checks for sym link loops using inodes instead of using realpath

2 files changed, 49 insertions(+), 32 deletions(-)

lib/std/fs.zig+11
...@@ -1545,6 +1545,17 @@ pub const Dir = struct {...@@ -1545,6 +1545,17 @@ pub const Dir = struct {
1545 return AtomicFile.init(dest_path, options.mode, self, false);1545 return AtomicFile.init(dest_path, options.mode, self, false);
1546 }1546 }
1547 }1547 }
1548
1549 pub const Stat = File.Stat;
1550 pub const StatError = File.StatError;
1551
1552 pub fn stat(self: Dir) StatError!Stat {
1553 const file: File = .{
1554 .handle = self.fd,
1555 .capable_io_mode = .blocking,
1556 };
1557 return file.stat();
1558 }
1548};1559};
15491560
1550/// Returns an handle to the current working directory. It is not opened with iteration capability.1561/// Returns an handle to the current working directory. It is not opened with iteration capability.
src-self-hosted/main.zig+38-32
...@@ -547,7 +547,7 @@ const Fmt = struct {...@@ -547,7 +547,7 @@ const Fmt = struct {
547 color: Color,547 color: Color,
548 gpa: *Allocator,548 gpa: *Allocator,
549549
550 const SeenMap = std.BufSet;550 const SeenMap = std.AutoHashMap(fs.File.INode, void);
551};551};
552552
553pub fn cmdFmt(gpa: *Allocator, args: []const []const u8) !void {553pub fn cmdFmt(gpa: *Allocator, args: []const []const u8) !void {
...@@ -644,7 +644,14 @@ pub fn cmdFmt(gpa: *Allocator, args: []const []const u8) !void {...@@ -644,7 +644,14 @@ pub fn cmdFmt(gpa: *Allocator, args: []const []const u8) !void {
644 };644 };
645645
646 for (input_files.span()) |file_path| {646 for (input_files.span()) |file_path| {
647 try fmtPath(&fmt, file_path, check_flag);647 // Get the real path here to avoid Windows failing on relative file paths with . or .. in them.
648 const real_path = fs.realpathAlloc(gpa, file_path) catch |err| {
649 std.debug.warn("unable to open '{}': {}\n", .{ file_path, err });
650 process.exit(1);
651 };
652 defer gpa.free(real_path);
653
654 try fmtPath(&fmt, file_path, check_flag, fs.cwd(), real_path);
648 }655 }
649 if (fmt.any_error) {656 if (fmt.any_error) {
650 process.exit(1);657 process.exit(1);
...@@ -673,20 +680,9 @@ const FmtError = error{...@@ -673,20 +680,9 @@ const FmtError = error{
673 EndOfStream,680 EndOfStream,
674} || fs.File.OpenError;681} || fs.File.OpenError;
675682
676fn fmtPath(fmt: *Fmt, file_path: []const u8, check_mode: bool) FmtError!void {683fn fmtPath(fmt: *Fmt, file_path: []const u8, check_mode: bool, dir: fs.Dir, sub_path: []const u8) FmtError!void {
677 // get the real path here to avoid Windows failing on relative file paths with . or .. in them684 fmtPathFile(fmt, file_path, check_mode, dir, sub_path) catch |err| switch (err) {
678 const real_path = fs.realpathAlloc(fmt.gpa, file_path) catch |err| {685 error.IsDir, error.AccessDenied => return fmtPathDir(fmt, file_path, check_mode, dir, sub_path),
679 std.debug.warn("unable to open '{}': {}\n", .{ file_path, err });
680 fmt.any_error = true;
681 return;
682 };
683 defer fmt.gpa.free(real_path);
684
685 if (fmt.seen.exists(real_path)) return;
686 try fmt.seen.put(real_path);
687
688 fmtPathFile(fmt, file_path, check_mode, real_path) catch |err| switch (err) {
689 error.IsDir, error.AccessDenied => return fmtPathDir(fmt, file_path, check_mode, real_path),
690 else => {686 else => {
691 std.debug.warn("unable to format '{}': {}\n", .{ file_path, err });687 std.debug.warn("unable to format '{}': {}\n", .{ file_path, err });
692 fmt.any_error = true;688 fmt.any_error = true;
...@@ -695,29 +691,30 @@ fn fmtPath(fmt: *Fmt, file_path: []const u8, check_mode: bool) FmtError!void {...@@ -695,29 +691,30 @@ fn fmtPath(fmt: *Fmt, file_path: []const u8, check_mode: bool) FmtError!void {
695 };691 };
696}692}
697693
698fn fmtPathDir(fmt: *Fmt, file_path: []const u8, check_mode: bool, parent_real_path: []const u8) FmtError!void {694fn fmtPathDir(
699 var dir = try fs.cwd().openDir(parent_real_path, .{ .iterate = true });695 fmt: *Fmt,
696 file_path: []const u8,
697 check_mode: bool,
698 parent_dir: fs.Dir,
699 parent_sub_path: []const u8,
700) FmtError!void {
701 var dir = try parent_dir.openDir(parent_sub_path, .{ .iterate = true });
700 defer dir.close();702 defer dir.close();
701703
704 const stat = try dir.stat();
705 if (try fmt.seen.put(stat.inode, {})) |_| return;
706
702 var dir_it = dir.iterate();707 var dir_it = dir.iterate();
703 while (try dir_it.next()) |entry| {708 while (try dir_it.next()) |entry| {
704 const is_dir = entry.kind == .Directory;709 const is_dir = entry.kind == .Directory;
705 if (is_dir or mem.endsWith(u8, entry.name, ".zig")) {710 if (is_dir or mem.endsWith(u8, entry.name, ".zig")) {
706 const full_path = try fs.path.join(fmt.gpa, &[_][]const u8{ file_path, entry.name });711 const full_path = try fs.path.join(fmt.gpa, &[_][]const u8{ file_path, entry.name });
707 const sub_real_path = fs.realpathAlloc(fmt.gpa, full_path) catch |err| {712 defer fmt.gpa.free(full_path);
708 std.debug.warn("unable to open '{}': {}\n", .{ file_path, err });
709 fmt.any_error = true;
710 return;
711 };
712 defer fmt.gpa.free(sub_real_path);
713
714 if (fmt.seen.exists(sub_real_path)) return;
715 try fmt.seen.put(sub_real_path);
716713
717 if (is_dir) {714 if (is_dir) {
718 try fmtPathDir(fmt, full_path, check_mode, sub_real_path);715 try fmtPathDir(fmt, full_path, check_mode, dir, entry.name);
719 } else {716 } else {
720 fmtPathFile(fmt, full_path, check_mode, sub_real_path) catch |err| {717 fmtPathFile(fmt, full_path, check_mode, dir, entry.name) catch |err| {
721 std.debug.warn("unable to format '{}': {}\n", .{ full_path, err });718 std.debug.warn("unable to format '{}': {}\n", .{ full_path, err });
722 fmt.any_error = true;719 fmt.any_error = true;
723 return;720 return;
...@@ -727,8 +724,14 @@ fn fmtPathDir(fmt: *Fmt, file_path: []const u8, check_mode: bool, parent_real_pa...@@ -727,8 +724,14 @@ fn fmtPathDir(fmt: *Fmt, file_path: []const u8, check_mode: bool, parent_real_pa
727 }724 }
728}725}
729726
730fn fmtPathFile(fmt: *Fmt, file_path: []const u8, check_mode: bool, real_path: []const u8) FmtError!void {727fn fmtPathFile(
731 const source_file = try fs.cwd().openFile(real_path, .{});728 fmt: *Fmt,
729 file_path: []const u8,
730 check_mode: bool,
731 dir: fs.Dir,
732 sub_path: []const u8,
733) FmtError!void {
734 const source_file = try dir.openFile(sub_path, .{});
732 defer source_file.close();735 defer source_file.close();
733736
734 const stat = try source_file.stat();737 const stat = try source_file.stat();
...@@ -743,6 +746,9 @@ fn fmtPathFile(fmt: *Fmt, file_path: []const u8, check_mode: bool, real_path: []...@@ -743,6 +746,9 @@ fn fmtPathFile(fmt: *Fmt, file_path: []const u8, check_mode: bool, real_path: []
743 };746 };
744 defer fmt.gpa.free(source_code);747 defer fmt.gpa.free(source_code);
745748
749 // Add to set after no longer possible to get error.IsDir.
750 if (try fmt.seen.put(stat.inode, {})) |_| return;
751
746 const tree = try std.zig.parse(fmt.gpa, source_code);752 const tree = try std.zig.parse(fmt.gpa, source_code);
747 defer tree.deinit();753 defer tree.deinit();
748754
...@@ -761,7 +767,7 @@ fn fmtPathFile(fmt: *Fmt, file_path: []const u8, check_mode: bool, real_path: []...@@ -761,7 +767,7 @@ fn fmtPathFile(fmt: *Fmt, file_path: []const u8, check_mode: bool, real_path: []
761 fmt.any_error = true;767 fmt.any_error = true;
762 }768 }
763 } else {769 } else {
764 const baf = try io.BufferedAtomicFile.create(fmt.gpa, fs.cwd(), real_path, .{ .mode = stat.mode });770 const baf = try io.BufferedAtomicFile.create(fmt.gpa, dir, sub_path, .{ .mode = stat.mode });
765 defer baf.destroy();771 defer baf.destroy();
766772
767 const anything_changed = try std.zig.render(fmt.gpa, baf.stream(), tree);773 const anything_changed = try std.zig.render(fmt.gpa, baf.stream(), tree);