diff --git a/lib/std/fs.zig b/lib/std/fs.zig index 3ff4819ac5..fa782b14c0 100644 --- a/lib/std/fs.zig +++ b/lib/std/fs.zig @@ -261,17 +261,7 @@ pub const Dir = struct { name: []const u8, kind: Kind, - pub const Kind = enum { - BlockDevice, - CharacterDevice, - Directory, - NamedPipe, - SymLink, - File, - UnixDomainSocket, - Whiteout, - Unknown, - }; + pub const Kind = File.Kind; }; const IteratorError = error{AccessDenied} || os.UnexpectedError; diff --git a/lib/std/fs/file.zig b/lib/std/fs/file.zig index 0a3c1b5ab7..f0304c332a 100644 --- a/lib/std/fs/file.zig +++ b/lib/std/fs/file.zig @@ -29,6 +29,18 @@ pub const File = struct { pub const Mode = os.mode_t; pub const INode = os.ino_t; + pub const Kind = enum { + BlockDevice, + CharacterDevice, + Directory, + NamedPipe, + SymLink, + File, + UnixDomainSocket, + Whiteout, + Unknown, + }; + pub const default_mode = switch (builtin.os.tag) { .windows => 0, .wasi => 0, @@ -219,13 +231,14 @@ pub const File = struct { /// unique across time, as some file systems may reuse an inode after its file has been deleted. /// Some systems may change the inode of a file over time. /// - /// On Linux, the inode _is_ structure that stores the metadata, and the inode _number_ is what + /// On Linux, the inode is a structure that stores the metadata, and the inode _number_ is what /// you see here: the index number of the inode. /// /// The FileIndex on Windows is similar. It is a number for a file that is unique to each filesystem. inode: INode, size: u64, mode: Mode, + kind: Kind, /// Access time in nanoseconds, relative to UTC 1970-01-01. atime: i128, @@ -254,6 +267,7 @@ pub const File = struct { .inode = info.InternalInformation.IndexNumber, .size = @bitCast(u64, info.StandardInformation.EndOfFile), .mode = 0, + .kind = if (info.StandardInformation.Directory == 0) .File else .Directory, .atime = windows.fromSysTime(info.BasicInformation.LastAccessTime), .mtime = windows.fromSysTime(info.BasicInformation.LastWriteTime), .ctime = windows.fromSysTime(info.BasicInformation.CreationTime), @@ -268,6 +282,16 @@ pub const File = struct { .inode = st.ino, .size = @bitCast(u64, st.size), .mode = st.mode, + .kind = switch (st.mode & os.S_IFMT) { + os.S_IFBLK => .BlockDevice, + os.S_IFCHR => .CharacterDevice, + os.S_IFDIR => .Directory, + os.S_IFIFO => .NamedPipe, + os.S_IFLNK => .SymLink, + os.S_IFREG => .File, + os.S_IFSOCK => .UnixDomainSocket, + else => .Unknown, + }, .atime = @as(i128, atime.tv_sec) * std.time.ns_per_s + atime.tv_nsec, .mtime = @as(i128, mtime.tv_sec) * std.time.ns_per_s + mtime.tv_nsec, .ctime = @as(i128, ctime.tv_sec) * std.time.ns_per_s + ctime.tv_nsec, diff --git a/src-self-hosted/main.zig b/src-self-hosted/main.zig index 3c81070439..ee4cf9bbe3 100644 --- a/src-self-hosted/main.zig +++ b/src-self-hosted/main.zig @@ -670,11 +670,12 @@ const FmtError = error{ ReadOnlyFileSystem, LinkQuotaExceeded, FileBusy, + EndOfStream, } || fs.File.OpenError; fn fmtPath(fmt: *Fmt, file_path: []const u8, check_mode: bool) FmtError!void { // get the real path here to avoid Windows failing on relative file paths with . or .. in them - var real_path = fs.realpathAlloc(fmt.gpa, file_path) catch |err| { + const real_path = fs.realpathAlloc(fmt.gpa, file_path) catch |err| { std.debug.warn("unable to open '{}': {}\n", .{ file_path, err }); fmt.any_error = true; return; @@ -684,47 +685,65 @@ fn fmtPath(fmt: *Fmt, file_path: []const u8, check_mode: bool) FmtError!void { if (fmt.seen.exists(real_path)) return; try fmt.seen.put(real_path); - const source_file = fs.cwd().openFile(real_path, .{}) catch |err| { - std.debug.warn("unable to open '{}': {}\n", .{ file_path, err }); - fmt.any_error = true; - return; - }; - defer source_file.close(); - - const stat = source_file.stat() catch |err| { - std.debug.warn("unable to stat '{}': {}\n", .{ file_path, err }); - fmt.any_error = true; - return; - }; - - const source_code = source_file.readAllAlloc(fmt.gpa, stat.size, max_src_size) catch |err| switch (err) { - error.IsDir => { - var dir = try fs.cwd().openDir(file_path, .{ .iterate = true }); - defer dir.close(); - - var dir_it = dir.iterate(); - - while (try dir_it.next()) |entry| { - if (entry.kind == .Directory or mem.endsWith(u8, entry.name, ".zig")) { - const full_path = try fs.path.join(fmt.gpa, &[_][]const u8{ file_path, entry.name }); - try fmtPath(fmt, full_path, check_mode); - } - } - return; - }, + fmtPathFile(fmt, file_path, check_mode, real_path) catch |err| switch (err) { + error.IsDir, error.AccessDenied => return fmtPathDir(fmt, file_path, check_mode, real_path), else => { - std.debug.warn("unable to read '{}': {}\n", .{ file_path, err }); + std.debug.warn("unable to format '{}': {}\n", .{ file_path, err }); fmt.any_error = true; return; }, }; +} + +fn fmtPathDir(fmt: *Fmt, file_path: []const u8, check_mode: bool, parent_real_path: []const u8) FmtError!void { + var dir = try fs.cwd().openDir(parent_real_path, .{ .iterate = true }); + defer dir.close(); + + var dir_it = dir.iterate(); + while (try dir_it.next()) |entry| { + const is_dir = entry.kind == .Directory; + if (is_dir or mem.endsWith(u8, entry.name, ".zig")) { + const full_path = try fs.path.join(fmt.gpa, &[_][]const u8{ file_path, entry.name }); + const sub_real_path = fs.realpathAlloc(fmt.gpa, full_path) catch |err| { + std.debug.warn("unable to open '{}': {}\n", .{ file_path, err }); + fmt.any_error = true; + return; + }; + defer fmt.gpa.free(sub_real_path); + + if (fmt.seen.exists(sub_real_path)) return; + try fmt.seen.put(sub_real_path); + + if (is_dir) { + try fmtPathDir(fmt, full_path, check_mode, sub_real_path); + } else { + fmtPathFile(fmt, full_path, check_mode, sub_real_path) catch |err| { + std.debug.warn("unable to format '{}': {}\n", .{ full_path, err }); + fmt.any_error = true; + return; + }; + } + } + } +} + +fn fmtPathFile(fmt: *Fmt, file_path: []const u8, check_mode: bool, real_path: []const u8) FmtError!void { + const source_file = try fs.cwd().openFile(real_path, .{}); + defer source_file.close(); + + const stat = try source_file.stat(); + + if (stat.kind == .Directory) + return error.IsDir; + + const source_code = source_file.readAllAlloc(fmt.gpa, stat.size, max_src_size) catch |err| switch (err) { + error.ConnectionResetByPeer => unreachable, + error.ConnectionTimedOut => unreachable, + else => |e| return e, + }; defer fmt.gpa.free(source_code); - const tree = std.zig.parse(fmt.gpa, source_code) catch |err| { - std.debug.warn("error parsing file '{}': {}\n", .{ file_path, err }); - fmt.any_error = true; - return; - }; + const tree = try std.zig.parse(fmt.gpa, source_code); defer tree.deinit(); for (tree.errors) |parse_error| {