diff --git a/lib/std/Io.zig b/lib/std/Io.zig index 0d27af1075..0d71727f3f 100644 --- a/lib/std/Io.zig +++ b/lib/std/Io.zig @@ -548,6 +548,7 @@ pub fn PollFiles(comptime StreamEnum: type) type { } test { + _ = net; _ = Reader; _ = Writer; _ = tty; @@ -688,42 +689,7 @@ pub const UnexpectedError = error{ Unexpected, }; -pub const Dir = struct { - handle: Handle, - - pub fn cwd() Dir { - return .{ .handle = std.fs.cwd().fd }; - } - - pub const Handle = std.posix.fd_t; - - pub fn openFile(dir: Dir, io: Io, sub_path: []const u8, flags: File.OpenFlags) File.OpenError!File { - return io.vtable.fileOpen(io.userdata, dir, sub_path, flags); - } - - pub fn createFile(dir: Dir, io: Io, sub_path: []const u8, flags: File.CreateFlags) File.OpenError!File { - return io.vtable.createFile(io.userdata, dir, sub_path, flags); - } - - pub const WriteFileOptions = struct { - /// On Windows, `sub_path` should be encoded as [WTF-8](https://simonsapin.github.io/wtf-8/). - /// On WASI, `sub_path` should be encoded as valid UTF-8. - /// On other platforms, `sub_path` is an opaque sequence of bytes with no particular encoding. - sub_path: []const u8, - data: []const u8, - flags: File.CreateFlags = .{}, - }; - - pub const WriteFileError = File.WriteError || File.OpenError || Cancelable; - - /// Writes content to the file system, using the file creation flags provided. - pub fn writeFile(dir: Dir, io: Io, options: WriteFileOptions) WriteFileError!void { - var file = try dir.createFile(io, options.sub_path, options.flags); - defer file.close(io); - try file.writeAll(io, options.data); - } -}; - +pub const Dir = @import("Io/Dir.zig"); pub const File = @import("Io/File.zig"); pub const Timestamp = enum(i96) { diff --git a/lib/std/Io/Dir.zig b/lib/std/Io/Dir.zig new file mode 100644 index 0000000000..b951ff71c2 --- /dev/null +++ b/lib/std/Io/Dir.zig @@ -0,0 +1,113 @@ +const Dir = @This(); + +const std = @import("../std.zig"); +const Io = std.Io; +const File = Io.File; + +handle: Handle, + +pub fn cwd() Dir { + return .{ .handle = std.fs.cwd().fd }; +} + +pub const Handle = std.posix.fd_t; + +pub fn openFile(dir: Dir, io: Io, sub_path: []const u8, flags: File.OpenFlags) File.OpenError!File { + return io.vtable.fileOpen(io.userdata, dir, sub_path, flags); +} + +pub fn createFile(dir: Dir, io: Io, sub_path: []const u8, flags: File.CreateFlags) File.OpenError!File { + return io.vtable.createFile(io.userdata, dir, sub_path, flags); +} + +pub const WriteFileOptions = struct { + /// On Windows, `sub_path` should be encoded as [WTF-8](https://simonsapin.github.io/wtf-8/). + /// On WASI, `sub_path` should be encoded as valid UTF-8. + /// On other platforms, `sub_path` is an opaque sequence of bytes with no particular encoding. + sub_path: []const u8, + data: []const u8, + flags: File.CreateFlags = .{}, +}; + +pub const WriteFileError = File.WriteError || File.OpenError || Io.Cancelable; + +/// Writes content to the file system, using the file creation flags provided. +pub fn writeFile(dir: Dir, io: Io, options: WriteFileOptions) WriteFileError!void { + var file = try dir.createFile(io, options.sub_path, options.flags); + defer file.close(io); + try file.writeAll(io, options.data); +} + +pub const PrevStatus = enum { + stale, + fresh, +}; + +pub const UpdateFileError = File.OpenError; + +/// Check the file size, mtime, and mode of `source_path` and `dest_path`. If +/// they are equal, does nothing. Otherwise, atomically copies `source_path` to +/// `dest_path`. The destination file gains the mtime, atime, and mode of the +/// source file so that the next call to `updateFile` will not need a copy. +/// +/// Returns the previous status of the file before updating. +/// +/// * On Windows, both paths should be encoded as [WTF-8](https://simonsapin.github.io/wtf-8/). +/// * On WASI, both paths should be encoded as valid UTF-8. +/// * On other platforms, both paths are an opaque sequence of bytes with no particular encoding. +pub fn updateFile( + source_dir: Dir, + io: Io, + source_path: []const u8, + dest_dir: Dir, + /// If directories in this path do not exist, they are created. + dest_path: []const u8, + options: std.fs.Dir.CopyFileOptions, +) !PrevStatus { + var src_file = try source_dir.openFile(io, source_path, .{}); + defer src_file.close(); + + const src_stat = try src_file.stat(io); + const actual_mode = options.override_mode orelse src_stat.mode; + check_dest_stat: { + const dest_stat = blk: { + var dest_file = dest_dir.openFile(io, dest_path, .{}) catch |err| switch (err) { + error.FileNotFound => break :check_dest_stat, + else => |e| return e, + }; + defer dest_file.close(io); + + break :blk try dest_file.stat(io); + }; + + if (src_stat.size == dest_stat.size and + src_stat.mtime == dest_stat.mtime and + actual_mode == dest_stat.mode) + { + return .fresh; + } + } + + if (std.fs.path.dirname(dest_path)) |dirname| { + try dest_dir.makePath(io, dirname); + } + + var buffer: [1000]u8 = undefined; // Used only when direct fd-to-fd is not available. + var atomic_file = try dest_dir.atomicFile(io, dest_path, .{ + .mode = actual_mode, + .write_buffer = &buffer, + }); + defer atomic_file.deinit(); + + var src_reader: File.Reader = .initSize(io, src_file, &.{}, src_stat.size); + const dest_writer = &atomic_file.file_writer.interface; + + _ = dest_writer.sendFileAll(&src_reader, .unlimited) catch |err| switch (err) { + error.ReadFailed => return src_reader.err.?, + error.WriteFailed => return atomic_file.file_writer.err.?, + }; + try atomic_file.flush(); + try atomic_file.file_writer.file.updateTimes(src_stat.atime, src_stat.mtime); + try atomic_file.renameIntoPlace(); + return .stale; +} diff --git a/lib/std/Io/File.zig b/lib/std/Io/File.zig index dbabaa70d1..c33b31d341 100644 --- a/lib/std/Io/File.zig +++ b/lib/std/Io/File.zig @@ -1,7 +1,9 @@ +const File = @This(); + const builtin = @import("builtin"); + const std = @import("../std.zig"); const Io = std.Io; -const File = @This(); const assert = std.debug.assert; handle: Handle, diff --git a/lib/std/Io/net.zig b/lib/std/Io/net.zig index aa5032ada8..0881fdc30c 100644 --- a/lib/std/Io/net.zig +++ b/lib/std/Io/net.zig @@ -593,3 +593,7 @@ pub const InterfaceIndexError = error{ pub fn interfaceIndex(io: Io, name: []const u8) InterfaceIndexError!u32 { return io.vtable.netInterfaceIndex(io.userdata, name); } + +test { + _ = HostName; +} diff --git a/lib/std/Io/net/HostName.zig b/lib/std/Io/net/HostName.zig index a1a8b48d8e..06cf4219b3 100644 --- a/lib/std/Io/net/HostName.zig +++ b/lib/std/Io/net/HostName.zig @@ -629,3 +629,28 @@ pub const ResolvConf = struct { @panic("TODO"); } }; + +test ResolvConf { + const input = + \\# Generated by resolvconf + \\nameserver 1.0.0.1 + \\nameserver 1.1.1.1 + \\nameserver fe80::e0e:76ff:fed4:cf22%eno1 + \\options edns0 + \\ + ; + var reader: Io.Reader = .fixed(input); + + var rc: ResolvConf = .{ + .nameservers_buffer = undefined, + .nameservers_len = 0, + .search_buffer = undefined, + .search_len = 0, + .ndots = 1, + .timeout = 5, + .attempts = 2, + }; + + try rc.parse(&reader); + try std.testing.expect(false); +} diff --git a/lib/std/fs.zig b/lib/std/fs.zig index 03fd679495..16bc320d32 100644 --- a/lib/std/fs.zig +++ b/lib/std/fs.zig @@ -106,13 +106,15 @@ pub fn updateFileAbsolute( source_path: []const u8, dest_path: []const u8, args: Dir.CopyFileOptions, -) !Dir.PrevStatus { +) !std.Io.Dir.PrevStatus { assert(path.isAbsolute(source_path)); assert(path.isAbsolute(dest_path)); const my_cwd = cwd(); return Dir.updateFile(my_cwd, source_path, my_cwd, dest_path, args); } +test updateFileAbsolute {} + /// Same as `Dir.copyFile`, except asserts that both `source_path` and `dest_path` /// are absolute. See `Dir.copyFile` for a function that operates on both /// absolute and relative paths. @@ -130,6 +132,8 @@ pub fn copyFileAbsolute( return Dir.copyFile(my_cwd, source_path, my_cwd, dest_path, args); } +test copyFileAbsolute {} + /// Create a new directory, based on an absolute path. /// Asserts that the path is absolute. See `Dir.makeDir` for a function that operates /// on both absolute and relative paths. @@ -141,12 +145,16 @@ pub fn makeDirAbsolute(absolute_path: []const u8) !void { return posix.mkdir(absolute_path, Dir.default_mode); } +test makeDirAbsolute {} + /// Same as `makeDirAbsolute` except the parameter is null-terminated. pub fn makeDirAbsoluteZ(absolute_path_z: [*:0]const u8) !void { assert(path.isAbsoluteZ(absolute_path_z)); return posix.mkdirZ(absolute_path_z, Dir.default_mode); } +test makeDirAbsoluteZ {} + /// Same as `makeDirAbsolute` except the parameter is a null-terminated WTF-16 LE-encoded string. pub fn makeDirAbsoluteW(absolute_path_w: [*:0]const u16) !void { assert(path.isAbsoluteWindowsW(absolute_path_w)); @@ -693,16 +701,10 @@ pub fn realpathAlloc(allocator: Allocator, pathname: []const u8) ![]u8 { } test { - if (native_os != .wasi) { - _ = &makeDirAbsolute; - _ = &makeDirAbsoluteZ; - _ = ©FileAbsolute; - _ = &updateFileAbsolute; - } - _ = &AtomicFile; - _ = &Dir; - _ = &File; - _ = &path; + _ = AtomicFile; + _ = Dir; + _ = File; + _ = path; _ = @import("fs/test.zig"); _ = @import("fs/get_app_data_dir.zig"); } diff --git a/lib/std/fs/Dir.zig b/lib/std/fs/Dir.zig index d097573122..d09cdfa896 100644 --- a/lib/std/fs/Dir.zig +++ b/lib/std/fs/Dir.zig @@ -2539,74 +2539,6 @@ pub const CopyFileOptions = struct { override_mode: ?File.Mode = null, }; -pub const PrevStatus = enum { - stale, - fresh, -}; - -/// Check the file size, mtime, and mode of `source_path` and `dest_path`. If they are equal, does nothing. -/// Otherwise, atomically copies `source_path` to `dest_path`. The destination file gains the mtime, -/// atime, and mode of the source file so that the next call to `updateFile` will not need a copy. -/// Returns the previous status of the file before updating. -/// If any of the directories do not exist for dest_path, they are created. -/// On Windows, both paths should be encoded as [WTF-8](https://simonsapin.github.io/wtf-8/). -/// On WASI, both paths should be encoded as valid UTF-8. -/// On other platforms, both paths are an opaque sequence of bytes with no particular encoding. -pub fn updateFile( - source_dir: Dir, - source_path: []const u8, - dest_dir: Dir, - dest_path: []const u8, - options: CopyFileOptions, -) !PrevStatus { - var src_file = try source_dir.openFile(source_path, .{}); - defer src_file.close(); - - const src_stat = try src_file.stat(); - const actual_mode = options.override_mode orelse src_stat.mode; - check_dest_stat: { - const dest_stat = blk: { - var dest_file = dest_dir.openFile(dest_path, .{}) catch |err| switch (err) { - error.FileNotFound => break :check_dest_stat, - else => |e| return e, - }; - defer dest_file.close(); - - break :blk try dest_file.stat(); - }; - - if (src_stat.size == dest_stat.size and - src_stat.mtime == dest_stat.mtime and - actual_mode == dest_stat.mode) - { - return PrevStatus.fresh; - } - } - - if (fs.path.dirname(dest_path)) |dirname| { - try dest_dir.makePath(dirname); - } - - var buffer: [1000]u8 = undefined; // Used only when direct fd-to-fd is not available. - var atomic_file = try dest_dir.atomicFile(dest_path, .{ - .mode = actual_mode, - .write_buffer = &buffer, - }); - defer atomic_file.deinit(); - - var src_reader: File.Reader = .initSize(src_file, &.{}, src_stat.size); - const dest_writer = &atomic_file.file_writer.interface; - - _ = dest_writer.sendFileAll(&src_reader, .unlimited) catch |err| switch (err) { - error.ReadFailed => return src_reader.err.?, - error.WriteFailed => return atomic_file.file_writer.err.?, - }; - try atomic_file.flush(); - try atomic_file.file_writer.file.updateTimes(src_stat.atime, src_stat.mtime); - try atomic_file.renameIntoPlace(); - return .stale; -} - pub const CopyFileError = File.OpenError || File.StatError || AtomicFile.InitError || AtomicFile.FinishError || File.ReadError || File.WriteError; diff --git a/lib/std/fs/File.zig b/lib/std/fs/File.zig index 01ad42167d..4af7468f59 100644 --- a/lib/std/fs/File.zig +++ b/lib/std/fs/File.zig @@ -1143,13 +1143,7 @@ pub const Reader = struct { fn stream(io_reader: *std.Io.Reader, w: *std.Io.Writer, limit: std.Io.Limit) std.Io.Reader.StreamError!usize { const r: *Reader = @alignCast(@fieldParentPtr("interface", io_reader)); switch (r.mode) { - .positional, .streaming => return w.sendFile(r, limit) catch |write_err| switch (write_err) { - error.Unimplemented => { - r.mode = r.mode.toReading(); - return 0; - }, - else => |e| return e, - }, + .positional, .streaming => @panic("TODO"), .positional_reading => { const dest = limit.slice(try w.writableSliceGreedy(1)); const n = try readPositional(r, dest); diff --git a/lib/std/posix.zig b/lib/std/posix.zig index 6030837545..1de148b4a5 100644 --- a/lib/std/posix.zig +++ b/lib/std/posix.zig @@ -940,7 +940,7 @@ pub fn readv(fd: fd_t, iov: []const iovec) ReadError!usize { } } -pub const PReadError = std.Io.ReadPositionalError; +pub const PReadError = std.Io.File.ReadPositionalError; /// Number of bytes read is returned. Upon reading end-of-file, zero is returned. ///