| author | |
| committer | |
| log | 43eea7beecaeb29d048cf971d6d08d0297109ce0 |
| tree | 715928a1485823da385abd27cb374bf9b3d84414 |
| parent | e34bb9a4131c2f0f328a53419d237877152917fa |
9 files changed, 162 insertions(+), 124 deletions(-)
lib/std/Io.zig+2-36| ... | ... | @@ -548,6 +548,7 @@ pub fn PollFiles(comptime StreamEnum: type) type { |
| 548 | 548 | } |
| 549 | 549 | |
| 550 | 550 | test { |
| 551 | _ = net; | |
| 551 | 552 | _ = Reader; |
| 552 | 553 | _ = Writer; |
| 553 | 554 | _ = tty; |
| ... | ... | @@ -688,42 +689,7 @@ pub const UnexpectedError = error{ |
| 688 | 689 | Unexpected, |
| 689 | 690 | }; |
| 690 | 691 | |
| 691 | pub const Dir = struct { | |
| 692 | handle: Handle, | |
| 693 | ||
| 694 | pub fn cwd() Dir { | |
| 695 | return .{ .handle = std.fs.cwd().fd }; | |
| 696 | } | |
| 697 | ||
| 698 | pub const Handle = std.posix.fd_t; | |
| 699 | ||
| 700 | pub fn openFile(dir: Dir, io: Io, sub_path: []const u8, flags: File.OpenFlags) File.OpenError!File { | |
| 701 | return io.vtable.fileOpen(io.userdata, dir, sub_path, flags); | |
| 702 | } | |
| 703 | ||
| 704 | pub fn createFile(dir: Dir, io: Io, sub_path: []const u8, flags: File.CreateFlags) File.OpenError!File { | |
| 705 | return io.vtable.createFile(io.userdata, dir, sub_path, flags); | |
| 706 | } | |
| 707 | ||
| 708 | pub const WriteFileOptions = struct { | |
| 709 | /// On Windows, `sub_path` should be encoded as [WTF-8](https://simonsapin.github.io/wtf-8/). | |
| 710 | /// On WASI, `sub_path` should be encoded as valid UTF-8. | |
| 711 | /// On other platforms, `sub_path` is an opaque sequence of bytes with no particular encoding. | |
| 712 | sub_path: []const u8, | |
| 713 | data: []const u8, | |
| 714 | flags: File.CreateFlags = .{}, | |
| 715 | }; | |
| 716 | ||
| 717 | pub const WriteFileError = File.WriteError || File.OpenError || Cancelable; | |
| 718 | ||
| 719 | /// Writes content to the file system, using the file creation flags provided. | |
| 720 | pub fn writeFile(dir: Dir, io: Io, options: WriteFileOptions) WriteFileError!void { | |
| 721 | var file = try dir.createFile(io, options.sub_path, options.flags); | |
| 722 | defer file.close(io); | |
| 723 | try file.writeAll(io, options.data); | |
| 724 | } | |
| 725 | }; | |
| 726 | ||
| 692 | pub const Dir = @import("Io/Dir.zig"); | |
| 727 | 693 | pub const File = @import("Io/File.zig"); |
| 728 | 694 | |
| 729 | 695 | pub const Timestamp = enum(i96) { |
lib/std/Io/Dir.zig created+113| ... | ... | @@ -0,0 +1,113 @@ |
| 1 | const Dir = @This(); | |
| 2 | ||
| 3 | const std = @import("../std.zig"); | |
| 4 | const Io = std.Io; | |
| 5 | const File = Io.File; | |
| 6 | ||
| 7 | handle: Handle, | |
| 8 | ||
| 9 | pub fn cwd() Dir { | |
| 10 | return .{ .handle = std.fs.cwd().fd }; | |
| 11 | } | |
| 12 | ||
| 13 | pub const Handle = std.posix.fd_t; | |
| 14 | ||
| 15 | pub fn openFile(dir: Dir, io: Io, sub_path: []const u8, flags: File.OpenFlags) File.OpenError!File { | |
| 16 | return io.vtable.fileOpen(io.userdata, dir, sub_path, flags); | |
| 17 | } | |
| 18 | ||
| 19 | pub fn createFile(dir: Dir, io: Io, sub_path: []const u8, flags: File.CreateFlags) File.OpenError!File { | |
| 20 | return io.vtable.createFile(io.userdata, dir, sub_path, flags); | |
| 21 | } | |
| 22 | ||
| 23 | pub const WriteFileOptions = struct { | |
| 24 | /// On Windows, `sub_path` should be encoded as [WTF-8](https://simonsapin.github.io/wtf-8/). | |
| 25 | /// On WASI, `sub_path` should be encoded as valid UTF-8. | |
| 26 | /// On other platforms, `sub_path` is an opaque sequence of bytes with no particular encoding. | |
| 27 | sub_path: []const u8, | |
| 28 | data: []const u8, | |
| 29 | flags: File.CreateFlags = .{}, | |
| 30 | }; | |
| 31 | ||
| 32 | pub const WriteFileError = File.WriteError || File.OpenError || Io.Cancelable; | |
| 33 | ||
| 34 | /// Writes content to the file system, using the file creation flags provided. | |
| 35 | pub fn writeFile(dir: Dir, io: Io, options: WriteFileOptions) WriteFileError!void { | |
| 36 | var file = try dir.createFile(io, options.sub_path, options.flags); | |
| 37 | defer file.close(io); | |
| 38 | try file.writeAll(io, options.data); | |
| 39 | } | |
| 40 | ||
| 41 | pub const PrevStatus = enum { | |
| 42 | stale, | |
| 43 | fresh, | |
| 44 | }; | |
| 45 | ||
| 46 | pub const UpdateFileError = File.OpenError; | |
| 47 | ||
| 48 | /// Check the file size, mtime, and mode of `source_path` and `dest_path`. If | |
| 49 | /// they are equal, does nothing. Otherwise, atomically copies `source_path` to | |
| 50 | /// `dest_path`. The destination file gains the mtime, atime, and mode of the | |
| 51 | /// source file so that the next call to `updateFile` will not need a copy. | |
| 52 | /// | |
| 53 | /// Returns the previous status of the file before updating. | |
| 54 | /// | |
| 55 | /// * On Windows, both paths should be encoded as [WTF-8](https://simonsapin.github.io/wtf-8/). | |
| 56 | /// * On WASI, both paths should be encoded as valid UTF-8. | |
| 57 | /// * On other platforms, both paths are an opaque sequence of bytes with no particular encoding. | |
| 58 | pub fn updateFile( | |
| 59 | source_dir: Dir, | |
| 60 | io: Io, | |
| 61 | source_path: []const u8, | |
| 62 | dest_dir: Dir, | |
| 63 | /// If directories in this path do not exist, they are created. | |
| 64 | dest_path: []const u8, | |
| 65 | options: std.fs.Dir.CopyFileOptions, | |
| 66 | ) !PrevStatus { | |
| 67 | var src_file = try source_dir.openFile(io, source_path, .{}); | |
| 68 | defer src_file.close(); | |
| 69 | ||
| 70 | const src_stat = try src_file.stat(io); | |
| 71 | const actual_mode = options.override_mode orelse src_stat.mode; | |
| 72 | check_dest_stat: { | |
| 73 | const dest_stat = blk: { | |
| 74 | var dest_file = dest_dir.openFile(io, dest_path, .{}) catch |err| switch (err) { | |
| 75 | error.FileNotFound => break :check_dest_stat, | |
| 76 | else => |e| return e, | |
| 77 | }; | |
| 78 | defer dest_file.close(io); | |
| 79 | ||
| 80 | break :blk try dest_file.stat(io); | |
| 81 | }; | |
| 82 | ||
| 83 | if (src_stat.size == dest_stat.size and | |
| 84 | src_stat.mtime == dest_stat.mtime and | |
| 85 | actual_mode == dest_stat.mode) | |
| 86 | { | |
| 87 | return .fresh; | |
| 88 | } | |
| 89 | } | |
| 90 | ||
| 91 | if (std.fs.path.dirname(dest_path)) |dirname| { | |
| 92 | try dest_dir.makePath(io, dirname); | |
| 93 | } | |
| 94 | ||
| 95 | var buffer: [1000]u8 = undefined; // Used only when direct fd-to-fd is not available. | |
| 96 | var atomic_file = try dest_dir.atomicFile(io, dest_path, .{ | |
| 97 | .mode = actual_mode, | |
| 98 | .write_buffer = &buffer, | |
| 99 | }); | |
| 100 | defer atomic_file.deinit(); | |
| 101 | ||
| 102 | var src_reader: File.Reader = .initSize(io, src_file, &.{}, src_stat.size); | |
| 103 | const dest_writer = &atomic_file.file_writer.interface; | |
| 104 | ||
| 105 | _ = dest_writer.sendFileAll(&src_reader, .unlimited) catch |err| switch (err) { | |
| 106 | error.ReadFailed => return src_reader.err.?, | |
| 107 | error.WriteFailed => return atomic_file.file_writer.err.?, | |
| 108 | }; | |
| 109 | try atomic_file.flush(); | |
| 110 | try atomic_file.file_writer.file.updateTimes(src_stat.atime, src_stat.mtime); | |
| 111 | try atomic_file.renameIntoPlace(); | |
| 112 | return .stale; | |
| 113 | } |
lib/std/Io/File.zig+3-1| ... | ... | @@ -1,7 +1,9 @@ |
| 1 | const File = @This(); | |
| 2 | ||
| 1 | 3 | const builtin = @import("builtin"); |
| 4 | ||
| 2 | 5 | const std = @import("../std.zig"); |
| 3 | 6 | const Io = std.Io; |
| 4 | const File = @This(); | |
| 5 | 7 | const assert = std.debug.assert; |
| 6 | 8 | |
| 7 | 9 | handle: Handle, |
lib/std/Io/net.zig+4| ... | ... | @@ -593,3 +593,7 @@ pub const InterfaceIndexError = error{ |
| 593 | 593 | pub fn interfaceIndex(io: Io, name: []const u8) InterfaceIndexError!u32 { |
| 594 | 594 | return io.vtable.netInterfaceIndex(io.userdata, name); |
| 595 | 595 | } |
| 596 | ||
| 597 | test { | |
| 598 | _ = HostName; | |
| 599 | } |
lib/std/Io/net/HostName.zig+25| ... | ... | @@ -629,3 +629,28 @@ pub const ResolvConf = struct { |
| 629 | 629 | @panic("TODO"); |
| 630 | 630 | } |
| 631 | 631 | }; |
| 632 | ||
| 633 | test ResolvConf { | |
| 634 | const input = | |
| 635 | \\# Generated by resolvconf | |
| 636 | \\nameserver 1.0.0.1 | |
| 637 | \\nameserver 1.1.1.1 | |
| 638 | \\nameserver fe80::e0e:76ff:fed4:cf22%eno1 | |
| 639 | \\options edns0 | |
| 640 | \\ | |
| 641 | ; | |
| 642 | var reader: Io.Reader = .fixed(input); | |
| 643 | ||
| 644 | var rc: ResolvConf = .{ | |
| 645 | .nameservers_buffer = undefined, | |
| 646 | .nameservers_len = 0, | |
| 647 | .search_buffer = undefined, | |
| 648 | .search_len = 0, | |
| 649 | .ndots = 1, | |
| 650 | .timeout = 5, | |
| 651 | .attempts = 2, | |
| 652 | }; | |
| 653 | ||
| 654 | try rc.parse(&reader); | |
| 655 | try std.testing.expect(false); | |
| 656 | } |
lib/std/fs.zig+13-11| ... | ... | @@ -106,13 +106,15 @@ pub fn updateFileAbsolute( |
| 106 | 106 | source_path: []const u8, |
| 107 | 107 | dest_path: []const u8, |
| 108 | 108 | args: Dir.CopyFileOptions, |
| 109 | ) !Dir.PrevStatus { | |
| 109 | ) !std.Io.Dir.PrevStatus { | |
| 110 | 110 | assert(path.isAbsolute(source_path)); |
| 111 | 111 | assert(path.isAbsolute(dest_path)); |
| 112 | 112 | const my_cwd = cwd(); |
| 113 | 113 | return Dir.updateFile(my_cwd, source_path, my_cwd, dest_path, args); |
| 114 | 114 | } |
| 115 | 115 | |
| 116 | test updateFileAbsolute {} | |
| 117 | ||
| 116 | 118 | /// Same as `Dir.copyFile`, except asserts that both `source_path` and `dest_path` |
| 117 | 119 | /// are absolute. See `Dir.copyFile` for a function that operates on both |
| 118 | 120 | /// absolute and relative paths. |
| ... | ... | @@ -130,6 +132,8 @@ pub fn copyFileAbsolute( |
| 130 | 132 | return Dir.copyFile(my_cwd, source_path, my_cwd, dest_path, args); |
| 131 | 133 | } |
| 132 | 134 | |
| 135 | test copyFileAbsolute {} | |
| 136 | ||
| 133 | 137 | /// Create a new directory, based on an absolute path. |
| 134 | 138 | /// Asserts that the path is absolute. See `Dir.makeDir` for a function that operates |
| 135 | 139 | /// on both absolute and relative paths. |
| ... | ... | @@ -141,12 +145,16 @@ pub fn makeDirAbsolute(absolute_path: []const u8) !void { |
| 141 | 145 | return posix.mkdir(absolute_path, Dir.default_mode); |
| 142 | 146 | } |
| 143 | 147 | |
| 148 | test makeDirAbsolute {} | |
| 149 | ||
| 144 | 150 | /// Same as `makeDirAbsolute` except the parameter is null-terminated. |
| 145 | 151 | pub fn makeDirAbsoluteZ(absolute_path_z: [*:0]const u8) !void { |
| 146 | 152 | assert(path.isAbsoluteZ(absolute_path_z)); |
| 147 | 153 | return posix.mkdirZ(absolute_path_z, Dir.default_mode); |
| 148 | 154 | } |
| 149 | 155 | |
| 156 | test makeDirAbsoluteZ {} | |
| 157 | ||
| 150 | 158 | /// Same as `makeDirAbsolute` except the parameter is a null-terminated WTF-16 LE-encoded string. |
| 151 | 159 | pub fn makeDirAbsoluteW(absolute_path_w: [*:0]const u16) !void { |
| 152 | 160 | assert(path.isAbsoluteWindowsW(absolute_path_w)); |
| ... | ... | @@ -693,16 +701,10 @@ pub fn realpathAlloc(allocator: Allocator, pathname: []const u8) ![]u8 { |
| 693 | 701 | } |
| 694 | 702 | |
| 695 | 703 | test { |
| 696 | if (native_os != .wasi) { | |
| 697 | _ = &makeDirAbsolute; | |
| 698 | _ = &makeDirAbsoluteZ; | |
| 699 | _ = &copyFileAbsolute; | |
| 700 | _ = &updateFileAbsolute; | |
| 701 | } | |
| 702 | _ = &AtomicFile; | |
| 703 | _ = &Dir; | |
| 704 | _ = &File; | |
| 705 | _ = &path; | |
| 704 | _ = AtomicFile; | |
| 705 | _ = Dir; | |
| 706 | _ = File; | |
| 707 | _ = path; | |
| 706 | 708 | _ = @import("fs/test.zig"); |
| 707 | 709 | _ = @import("fs/get_app_data_dir.zig"); |
| 708 | 710 | } |
lib/std/fs/Dir.zig-68| ... | ... | @@ -2539,74 +2539,6 @@ pub const CopyFileOptions = struct { |
| 2539 | 2539 | override_mode: ?File.Mode = null, |
| 2540 | 2540 | }; |
| 2541 | 2541 | |
| 2542 | pub const PrevStatus = enum { | |
| 2543 | stale, | |
| 2544 | fresh, | |
| 2545 | }; | |
| 2546 | ||
| 2547 | /// Check the file size, mtime, and mode of `source_path` and `dest_path`. If they are equal, does nothing. | |
| 2548 | /// Otherwise, atomically copies `source_path` to `dest_path`. The destination file gains the mtime, | |
| 2549 | /// atime, and mode of the source file so that the next call to `updateFile` will not need a copy. | |
| 2550 | /// Returns the previous status of the file before updating. | |
| 2551 | /// If any of the directories do not exist for dest_path, they are created. | |
| 2552 | /// On Windows, both paths should be encoded as [WTF-8](https://simonsapin.github.io/wtf-8/). | |
| 2553 | /// On WASI, both paths should be encoded as valid UTF-8. | |
| 2554 | /// On other platforms, both paths are an opaque sequence of bytes with no particular encoding. | |
| 2555 | pub fn updateFile( | |
| 2556 | source_dir: Dir, | |
| 2557 | source_path: []const u8, | |
| 2558 | dest_dir: Dir, | |
| 2559 | dest_path: []const u8, | |
| 2560 | options: CopyFileOptions, | |
| 2561 | ) !PrevStatus { | |
| 2562 | var src_file = try source_dir.openFile(source_path, .{}); | |
| 2563 | defer src_file.close(); | |
| 2564 | ||
| 2565 | const src_stat = try src_file.stat(); | |
| 2566 | const actual_mode = options.override_mode orelse src_stat.mode; | |
| 2567 | check_dest_stat: { | |
| 2568 | const dest_stat = blk: { | |
| 2569 | var dest_file = dest_dir.openFile(dest_path, .{}) catch |err| switch (err) { | |
| 2570 | error.FileNotFound => break :check_dest_stat, | |
| 2571 | else => |e| return e, | |
| 2572 | }; | |
| 2573 | defer dest_file.close(); | |
| 2574 | ||
| 2575 | break :blk try dest_file.stat(); | |
| 2576 | }; | |
| 2577 | ||
| 2578 | if (src_stat.size == dest_stat.size and | |
| 2579 | src_stat.mtime == dest_stat.mtime and | |
| 2580 | actual_mode == dest_stat.mode) | |
| 2581 | { | |
| 2582 | return PrevStatus.fresh; | |
| 2583 | } | |
| 2584 | } | |
| 2585 | ||
| 2586 | if (fs.path.dirname(dest_path)) |dirname| { | |
| 2587 | try dest_dir.makePath(dirname); | |
| 2588 | } | |
| 2589 | ||
| 2590 | var buffer: [1000]u8 = undefined; // Used only when direct fd-to-fd is not available. | |
| 2591 | var atomic_file = try dest_dir.atomicFile(dest_path, .{ | |
| 2592 | .mode = actual_mode, | |
| 2593 | .write_buffer = &buffer, | |
| 2594 | }); | |
| 2595 | defer atomic_file.deinit(); | |
| 2596 | ||
| 2597 | var src_reader: File.Reader = .initSize(src_file, &.{}, src_stat.size); | |
| 2598 | const dest_writer = &atomic_file.file_writer.interface; | |
| 2599 | ||
| 2600 | _ = dest_writer.sendFileAll(&src_reader, .unlimited) catch |err| switch (err) { | |
| 2601 | error.ReadFailed => return src_reader.err.?, | |
| 2602 | error.WriteFailed => return atomic_file.file_writer.err.?, | |
| 2603 | }; | |
| 2604 | try atomic_file.flush(); | |
| 2605 | try atomic_file.file_writer.file.updateTimes(src_stat.atime, src_stat.mtime); | |
| 2606 | try atomic_file.renameIntoPlace(); | |
| 2607 | return .stale; | |
| 2608 | } | |
| 2609 | ||
| 2610 | 2542 | pub const CopyFileError = File.OpenError || File.StatError || |
| 2611 | 2543 | AtomicFile.InitError || AtomicFile.FinishError || |
| 2612 | 2544 | File.ReadError || File.WriteError; |
lib/std/fs/File.zig+1-7| ... | ... | @@ -1143,13 +1143,7 @@ pub const Reader = struct { |
| 1143 | 1143 | fn stream(io_reader: *std.Io.Reader, w: *std.Io.Writer, limit: std.Io.Limit) std.Io.Reader.StreamError!usize { |
| 1144 | 1144 | const r: *Reader = @alignCast(@fieldParentPtr("interface", io_reader)); |
| 1145 | 1145 | switch (r.mode) { |
| 1146 | .positional, .streaming => return w.sendFile(r, limit) catch |write_err| switch (write_err) { | |
| 1147 | error.Unimplemented => { | |
| 1148 | r.mode = r.mode.toReading(); | |
| 1149 | return 0; | |
| 1150 | }, | |
| 1151 | else => |e| return e, | |
| 1152 | }, | |
| 1146 | .positional, .streaming => @panic("TODO"), | |
| 1153 | 1147 | .positional_reading => { |
| 1154 | 1148 | const dest = limit.slice(try w.writableSliceGreedy(1)); |
| 1155 | 1149 | const n = try readPositional(r, dest); |
lib/std/posix.zig+1-1| ... | ... | @@ -940,7 +940,7 @@ pub fn readv(fd: fd_t, iov: []const iovec) ReadError!usize { |
| 940 | 940 | } |
| 941 | 941 | } |
| 942 | 942 | |
| 943 | pub const PReadError = std.Io.ReadPositionalError; | |
| 943 | pub const PReadError = std.Io.File.ReadPositionalError; | |
| 944 | 944 | |
| 945 | 945 | /// Number of bytes read is returned. Upon reading end-of-file, zero is returned. |
| 946 | 946 | /// |