| author | |
| committer | |
| log | 11b8d3ce9d7bc8e3ab16813fd5415c9d45dbb232 |
| tree | 879d454b8c77d8a9dfeb4044ab55f66e513e7810 |
| parent | 85e1e3b95f1f1699a842a5e889d8987692a829a4 |
| parent | 2e7350140d4fbf9335e48693ccc4cba77d229c84 |
| signature |
15 files changed, 368 insertions(+), 166 deletions(-)
lib/std/build.zig+1-1| ... | @@ -2416,7 +2416,7 @@ fn findVcpkgRoot(allocator: *Allocator) !?[]const u8 { | ... | @@ -2416,7 +2416,7 @@ fn findVcpkgRoot(allocator: *Allocator) !?[]const u8 { |
| 2416 | const path_file = try fs.path.join(allocator, [_][]const u8{ appdata_path, "vcpkg.path.txt" }); | 2416 | const path_file = try fs.path.join(allocator, [_][]const u8{ appdata_path, "vcpkg.path.txt" }); |
| 2417 | defer allocator.free(path_file); | 2417 | defer allocator.free(path_file); |
| 2418 | 2418 | ||
| 2419 | const file = fs.File.openRead(path_file) catch return null; | 2419 | const file = fs.cwd().openFile(path_file, .{}) catch return null; |
| 2420 | defer file.close(); | 2420 | defer file.close(); |
| 2421 | 2421 | ||
| 2422 | const size = @intCast(usize, try file.getEndPos()); | 2422 | const size = @intCast(usize, try file.getEndPos()); |
lib/std/debug.zig+2-2| ... | @@ -1131,7 +1131,7 @@ fn openSelfDebugInfoMacOs(allocator: *mem.Allocator) !DebugInfo { | ... | @@ -1131,7 +1131,7 @@ fn openSelfDebugInfoMacOs(allocator: *mem.Allocator) !DebugInfo { |
| 1131 | } | 1131 | } |
| 1132 | 1132 | ||
| 1133 | fn printLineFromFileAnyOs(out_stream: var, line_info: LineInfo) !void { | 1133 | fn printLineFromFileAnyOs(out_stream: var, line_info: LineInfo) !void { |
| 1134 | var f = try File.openRead(line_info.file_name); | 1134 | var f = try fs.cwd().openFile(line_info.file_name, .{}); |
| 1135 | defer f.close(); | 1135 | defer f.close(); |
| 1136 | // TODO fstat and make sure that the file has the correct size | 1136 | // TODO fstat and make sure that the file has the correct size |
| 1137 | 1137 | ||
| ... | @@ -2089,7 +2089,7 @@ fn getLineNumberInfoMacOs(di: *DebugInfo, symbol: MachoSymbol, target_address: u | ... | @@ -2089,7 +2089,7 @@ fn getLineNumberInfoMacOs(di: *DebugInfo, symbol: MachoSymbol, target_address: u |
| 2089 | const ofile_path = mem.toSliceConst(u8, @ptrCast([*:0]const u8, di.strings.ptr + ofile.n_strx)); | 2089 | const ofile_path = mem.toSliceConst(u8, @ptrCast([*:0]const u8, di.strings.ptr + ofile.n_strx)); |
| 2090 | 2090 | ||
| 2091 | gop.kv.value = MachOFile{ | 2091 | gop.kv.value = MachOFile{ |
| 2092 | .bytes = try std.fs.Dir.cwd().readFileAllocAligned( | 2092 | .bytes = try std.fs.cwd().readFileAllocAligned( |
| 2093 | di.ofiles.allocator, | 2093 | di.ofiles.allocator, |
| 2094 | ofile_path, | 2094 | ofile_path, |
| 2095 | maxInt(usize), | 2095 | maxInt(usize), |
lib/std/fs.zig+229-49| ... | @@ -13,8 +13,6 @@ pub const File = @import("fs/file.zig").File; | ... | @@ -13,8 +13,6 @@ pub const File = @import("fs/file.zig").File; |
| 13 | 13 | ||
| 14 | pub const symLink = os.symlink; | 14 | pub const symLink = os.symlink; |
| 15 | pub const symLinkC = os.symlinkC; | 15 | pub const symLinkC = os.symlinkC; |
| 16 | pub const deleteFile = os.unlink; | ||
| 17 | pub const deleteFileC = os.unlinkC; | ||
| 18 | pub const rename = os.rename; | 16 | pub const rename = os.rename; |
| 19 | pub const renameC = os.renameC; | 17 | pub const renameC = os.renameC; |
| 20 | pub const renameW = os.renameW; | 18 | pub const renameW = os.renameW; |
| ... | @@ -88,13 +86,15 @@ pub fn updateFile(source_path: []const u8, dest_path: []const u8) !PrevStatus { | ... | @@ -88,13 +86,15 @@ pub fn updateFile(source_path: []const u8, dest_path: []const u8) !PrevStatus { |
| 88 | /// If any of the directories do not exist for dest_path, they are created. | 86 | /// If any of the directories do not exist for dest_path, they are created. |
| 89 | /// TODO https://github.com/ziglang/zig/issues/2885 | 87 | /// TODO https://github.com/ziglang/zig/issues/2885 |
| 90 | pub fn updateFileMode(source_path: []const u8, dest_path: []const u8, mode: ?File.Mode) !PrevStatus { | 88 | pub fn updateFileMode(source_path: []const u8, dest_path: []const u8, mode: ?File.Mode) !PrevStatus { |
| 91 | var src_file = try File.openRead(source_path); | 89 | const my_cwd = cwd(); |
| 90 | |||
| 91 | var src_file = try my_cwd.openFile(source_path, .{}); | ||
| 92 | defer src_file.close(); | 92 | defer src_file.close(); |
| 93 | 93 | ||
| 94 | const src_stat = try src_file.stat(); | 94 | const src_stat = try src_file.stat(); |
| 95 | check_dest_stat: { | 95 | check_dest_stat: { |
| 96 | const dest_stat = blk: { | 96 | const dest_stat = blk: { |
| 97 | var dest_file = File.openRead(dest_path) catch |err| switch (err) { | 97 | var dest_file = my_cwd.openFile(dest_path, .{}) catch |err| switch (err) { |
| 98 | error.FileNotFound => break :check_dest_stat, | 98 | error.FileNotFound => break :check_dest_stat, |
| 99 | else => |e| return e, | 99 | else => |e| return e, |
| 100 | }; | 100 | }; |
| ... | @@ -157,7 +157,7 @@ pub fn updateFileMode(source_path: []const u8, dest_path: []const u8, mode: ?Fil | ... | @@ -157,7 +157,7 @@ pub fn updateFileMode(source_path: []const u8, dest_path: []const u8, mode: ?Fil |
| 157 | /// in the same directory as dest_path. | 157 | /// in the same directory as dest_path. |
| 158 | /// Destination file will have the same mode as the source file. | 158 | /// Destination file will have the same mode as the source file. |
| 159 | pub fn copyFile(source_path: []const u8, dest_path: []const u8) !void { | 159 | pub fn copyFile(source_path: []const u8, dest_path: []const u8) !void { |
| 160 | var in_file = try File.openRead(source_path); | 160 | var in_file = try cwd().openFile(source_path, .{}); |
| 161 | defer in_file.close(); | 161 | defer in_file.close(); |
| 162 | 162 | ||
| 163 | const mode = try in_file.mode(); | 163 | const mode = try in_file.mode(); |
| ... | @@ -180,7 +180,7 @@ pub fn copyFile(source_path: []const u8, dest_path: []const u8) !void { | ... | @@ -180,7 +180,7 @@ pub fn copyFile(source_path: []const u8, dest_path: []const u8) !void { |
| 180 | /// merged and readily available, | 180 | /// merged and readily available, |
| 181 | /// there is a possibility of power loss or application termination leaving temporary files present | 181 | /// there is a possibility of power loss or application termination leaving temporary files present |
| 182 | pub fn copyFileMode(source_path: []const u8, dest_path: []const u8, mode: File.Mode) !void { | 182 | pub fn copyFileMode(source_path: []const u8, dest_path: []const u8, mode: File.Mode) !void { |
| 183 | var in_file = try File.openRead(source_path); | 183 | var in_file = try cwd().openFile(source_path, .{}); |
| 184 | defer in_file.close(); | 184 | defer in_file.close(); |
| 185 | 185 | ||
| 186 | var atomic_file = try AtomicFile.init(dest_path, mode); | 186 | var atomic_file = try AtomicFile.init(dest_path, mode); |
| ... | @@ -206,8 +206,6 @@ pub const AtomicFile = struct { | ... | @@ -206,8 +206,6 @@ pub const AtomicFile = struct { |
| 206 | 206 | ||
| 207 | /// dest_path must remain valid for the lifetime of AtomicFile | 207 | /// dest_path must remain valid for the lifetime of AtomicFile |
| 208 | /// call finish to atomically replace dest_path with contents | 208 | /// call finish to atomically replace dest_path with contents |
| 209 | /// TODO once we have null terminated pointers, use the | ||
| 210 | /// openWriteNoClobberN function | ||
| 211 | pub fn init(dest_path: []const u8, mode: File.Mode) InitError!AtomicFile { | 209 | pub fn init(dest_path: []const u8, mode: File.Mode) InitError!AtomicFile { |
| 212 | const dirname = path.dirname(dest_path); | 210 | const dirname = path.dirname(dest_path); |
| 213 | var rand_buf: [12]u8 = undefined; | 211 | var rand_buf: [12]u8 = undefined; |
| ... | @@ -224,15 +222,19 @@ pub const AtomicFile = struct { | ... | @@ -224,15 +222,19 @@ pub const AtomicFile = struct { |
| 224 | 222 | ||
| 225 | tmp_path_buf[tmp_path_len] = 0; | 223 | tmp_path_buf[tmp_path_len] = 0; |
| 226 | 224 | ||
| 225 | const my_cwd = cwd(); | ||
| 226 | |||
| 227 | while (true) { | 227 | while (true) { |
| 228 | try crypto.randomBytes(rand_buf[0..]); | 228 | try crypto.randomBytes(rand_buf[0..]); |
| 229 | b64_fs_encoder.encode(tmp_path_buf[dirname_component_len..tmp_path_len], rand_buf); | 229 | b64_fs_encoder.encode(tmp_path_buf[dirname_component_len..tmp_path_len], rand_buf); |
| 230 | 230 | ||
| 231 | const file = File.openWriteNoClobberC(@ptrCast([*:0]u8, &tmp_path_buf), mode) catch |err| switch (err) { | 231 | // TODO https://github.com/ziglang/zig/issues/3770 to clean up this @ptrCast |
| 232 | const file = my_cwd.createFileC( | ||
| 233 | @ptrCast([*:0]u8, &tmp_path_buf), | ||
| 234 | .{ .mode = mode, .exclusive = true }, | ||
| 235 | ) catch |err| switch (err) { | ||
| 232 | error.PathAlreadyExists => continue, | 236 | error.PathAlreadyExists => continue, |
| 233 | // TODO zig should figure out that this error set does not include PathAlreadyExists since | 237 | else => |e| return e, |
| 234 | // it is handled in the above switch | ||
| 235 | else => return err, | ||
| 236 | }; | 238 | }; |
| 237 | 239 | ||
| 238 | return AtomicFile{ | 240 | return AtomicFile{ |
| ... | @@ -248,7 +250,7 @@ pub const AtomicFile = struct { | ... | @@ -248,7 +250,7 @@ pub const AtomicFile = struct { |
| 248 | pub fn deinit(self: *AtomicFile) void { | 250 | pub fn deinit(self: *AtomicFile) void { |
| 249 | if (!self.finished) { | 251 | if (!self.finished) { |
| 250 | self.file.close(); | 252 | self.file.close(); |
| 251 | deleteFileC(@ptrCast([*:0]u8, &self.tmp_path_buf)) catch {}; | 253 | cwd().deleteFileC(@ptrCast([*:0]u8, &self.tmp_path_buf)) catch {}; |
| 252 | self.finished = true; | 254 | self.finished = true; |
| 253 | } | 255 | } |
| 254 | } | 256 | } |
| ... | @@ -350,12 +352,12 @@ pub fn deleteTree(full_path: []const u8) !void { | ... | @@ -350,12 +352,12 @@ pub fn deleteTree(full_path: []const u8) !void { |
| 350 | CannotDeleteRootDirectory, | 352 | CannotDeleteRootDirectory, |
| 351 | }.CannotDeleteRootDirectory; | 353 | }.CannotDeleteRootDirectory; |
| 352 | 354 | ||
| 353 | var dir = try Dir.cwd().openDirList(dirname); | 355 | var dir = try cwd().openDirList(dirname); |
| 354 | defer dir.close(); | 356 | defer dir.close(); |
| 355 | 357 | ||
| 356 | return dir.deleteTree(path.basename(full_path)); | 358 | return dir.deleteTree(path.basename(full_path)); |
| 357 | } else { | 359 | } else { |
| 358 | return Dir.cwd().deleteTree(full_path); | 360 | return cwd().deleteTree(full_path); |
| 359 | } | 361 | } |
| 360 | } | 362 | } |
| 361 | 363 | ||
| ... | @@ -657,17 +659,6 @@ pub const Dir = struct { | ... | @@ -657,17 +659,6 @@ pub const Dir = struct { |
| 657 | } | 659 | } |
| 658 | } | 660 | } |
| 659 | 661 | ||
| 660 | /// Returns an handle to the current working directory that is open for traversal. | ||
| 661 | /// Closing the returned `Dir` is checked illegal behavior. Iterating over the result is illegal behavior. | ||
| 662 | /// On POSIX targets, this function is comptime-callable. | ||
| 663 | pub fn cwd() Dir { | ||
| 664 | if (builtin.os == .windows) { | ||
| 665 | return Dir{ .fd = os.windows.peb().ProcessParameters.CurrentDirectory.Handle }; | ||
| 666 | } else { | ||
| 667 | return Dir{ .fd = os.AT_FDCWD }; | ||
| 668 | } | ||
| 669 | } | ||
| 670 | |||
| 671 | pub const OpenError = error{ | 662 | pub const OpenError = error{ |
| 672 | FileNotFound, | 663 | FileNotFound, |
| 673 | NotDir, | 664 | NotDir, |
| ... | @@ -683,12 +674,12 @@ pub const Dir = struct { | ... | @@ -683,12 +674,12 @@ pub const Dir = struct { |
| 683 | DeviceBusy, | 674 | DeviceBusy, |
| 684 | } || os.UnexpectedError; | 675 | } || os.UnexpectedError; |
| 685 | 676 | ||
| 686 | /// Deprecated; call `Dir.cwd().openDirList` directly. | 677 | /// Deprecated; call `cwd().openDirList` directly. |
| 687 | pub fn open(dir_path: []const u8) OpenError!Dir { | 678 | pub fn open(dir_path: []const u8) OpenError!Dir { |
| 688 | return cwd().openDirList(dir_path); | 679 | return cwd().openDirList(dir_path); |
| 689 | } | 680 | } |
| 690 | 681 | ||
| 691 | /// Deprecated; call `Dir.cwd().openDirListC` directly. | 682 | /// Deprecated; call `cwd().openDirListC` directly. |
| 692 | pub fn openC(dir_path_c: [*:0]const u8) OpenError!Dir { | 683 | pub fn openC(dir_path_c: [*:0]const u8) OpenError!Dir { |
| 693 | return cwd().openDirListC(dir_path_c); | 684 | return cwd().openDirListC(dir_path_c); |
| 694 | } | 685 | } |
| ... | @@ -698,29 +689,110 @@ pub const Dir = struct { | ... | @@ -698,29 +689,110 @@ pub const Dir = struct { |
| 698 | self.* = undefined; | 689 | self.* = undefined; |
| 699 | } | 690 | } |
| 700 | 691 | ||
| 701 | /// Call `File.close` on the result when done. | 692 | /// Opens a file for reading or writing, without attempting to create a new file. |
| 702 | pub fn openRead(self: Dir, sub_path: []const u8) File.OpenError!File { | 693 | /// Call `File.close` to release the resource. |
| 694 | /// Asserts that the path parameter has no null bytes. | ||
| 695 | pub fn openFile(self: Dir, sub_path: []const u8, flags: File.OpenFlags) File.OpenError!File { | ||
| 696 | if (std.debug.runtime_safety) for (sub_path) |byte| assert(byte != 0); | ||
| 703 | if (builtin.os == .windows) { | 697 | if (builtin.os == .windows) { |
| 704 | const path_w = try os.windows.sliceToPrefixedFileW(sub_path); | 698 | const path_w = try os.windows.sliceToPrefixedFileW(sub_path); |
| 705 | return self.openReadW(&path_w); | 699 | return self.openFileW(&path_w, flags); |
| 706 | } | 700 | } |
| 707 | const path_c = try os.toPosixPath(sub_path); | 701 | const path_c = try os.toPosixPath(sub_path); |
| 708 | return self.openReadC(&path_c); | 702 | return self.openFileC(&path_c, flags); |
| 709 | } | 703 | } |
| 710 | 704 | ||
| 711 | /// Call `File.close` on the result when done. | 705 | /// Same as `openFile` but the path parameter is null-terminated. |
| 712 | pub fn openReadC(self: Dir, sub_path: [*:0]const u8) File.OpenError!File { | 706 | pub fn openFileC(self: Dir, sub_path: [*:0]const u8, flags: File.OpenFlags) File.OpenError!File { |
| 713 | if (builtin.os == .windows) { | 707 | if (builtin.os == .windows) { |
| 714 | const path_w = try os.windows.cStrToPrefixedFileW(sub_path); | 708 | const path_w = try os.windows.cStrToPrefixedFileW(sub_path); |
| 715 | return self.openReadW(&path_w); | 709 | return self.openFileW(&path_w, flags); |
| 716 | } | 710 | } |
| 717 | const O_LARGEFILE = if (@hasDecl(os, "O_LARGEFILE")) os.O_LARGEFILE else 0; | 711 | const O_LARGEFILE = if (@hasDecl(os, "O_LARGEFILE")) os.O_LARGEFILE else 0; |
| 718 | const flags = O_LARGEFILE | os.O_RDONLY | os.O_CLOEXEC; | 712 | const os_flags = O_LARGEFILE | os.O_CLOEXEC | if (flags.write and flags.read) |
| 719 | const fd = try os.openatC(self.fd, sub_path, flags, 0); | 713 | @as(u32, os.O_RDWR) |
| 720 | return File.openHandle(fd); | 714 | else if (flags.write) |
| 715 | @as(u32, os.O_WRONLY) | ||
| 716 | else | ||
| 717 | @as(u32, os.O_RDONLY); | ||
| 718 | const fd = try os.openatC(self.fd, sub_path, os_flags, 0); | ||
| 719 | return File{ .handle = fd }; | ||
| 720 | } | ||
| 721 | |||
| 722 | /// Same as `openFile` but Windows-only and the path parameter is | ||
| 723 | /// [WTF-16](https://simonsapin.github.io/wtf-8/#potentially-ill-formed-utf-16) encoded. | ||
| 724 | pub fn openFileW(self: Dir, sub_path_w: [*:0]const u16, flags: File.OpenFlags) File.OpenError!File { | ||
| 725 | const w = os.windows; | ||
| 726 | const access_mask = w.SYNCHRONIZE | | ||
| 727 | (if (flags.read) @as(u32, w.GENERIC_READ) else 0) | | ||
| 728 | (if (flags.write) @as(u32, w.GENERIC_WRITE) else 0); | ||
| 729 | return self.openFileWindows(sub_path_w, access_mask, w.FILE_OPEN); | ||
| 721 | } | 730 | } |
| 722 | 731 | ||
| 723 | pub fn openReadW(self: Dir, sub_path_w: [*:0]const u16) File.OpenError!File { | 732 | /// Creates, opens, or overwrites a file with write access. |
| 733 | /// Call `File.close` on the result when done. | ||
| 734 | /// Asserts that the path parameter has no null bytes. | ||
| 735 | pub fn createFile(self: Dir, sub_path: []const u8, flags: File.CreateFlags) File.OpenError!File { | ||
| 736 | if (std.debug.runtime_safety) for (sub_path) |byte| assert(byte != 0); | ||
| 737 | if (builtin.os == .windows) { | ||
| 738 | const path_w = try os.windows.sliceToPrefixedFileW(sub_path); | ||
| 739 | return self.createFileW(&path_w, flags); | ||
| 740 | } | ||
| 741 | const path_c = try os.toPosixPath(sub_path); | ||
| 742 | return self.createFileC(&path_c, flags); | ||
| 743 | } | ||
| 744 | |||
| 745 | /// Same as `createFile` but the path parameter is null-terminated. | ||
| 746 | pub fn createFileC(self: Dir, sub_path_c: [*:0]const u8, flags: File.CreateFlags) File.OpenError!File { | ||
| 747 | if (builtin.os == .windows) { | ||
| 748 | const path_w = try os.windows.cStrToPrefixedFileW(sub_path_c); | ||
| 749 | return self.createFileW(&path_w, flags); | ||
| 750 | } | ||
| 751 | const O_LARGEFILE = if (@hasDecl(os, "O_LARGEFILE")) os.O_LARGEFILE else 0; | ||
| 752 | const os_flags = O_LARGEFILE | os.O_CREAT | os.O_CLOEXEC | | ||
| 753 | (if (flags.truncate) @as(u32, os.O_TRUNC) else 0) | | ||
| 754 | (if (flags.read) @as(u32, os.O_RDWR) else os.O_WRONLY) | | ||
| 755 | (if (flags.exclusive) @as(u32, os.O_EXCL) else 0); | ||
| 756 | const fd = try os.openatC(self.fd, sub_path_c, os_flags, flags.mode); | ||
| 757 | return File{ .handle = fd }; | ||
| 758 | } | ||
| 759 | |||
| 760 | /// Same as `createFile` but Windows-only and the path parameter is | ||
| 761 | /// [WTF-16](https://simonsapin.github.io/wtf-8/#potentially-ill-formed-utf-16) encoded. | ||
| 762 | pub fn createFileW(self: Dir, sub_path_w: [*:0]const u16, flags: File.CreateFlags) File.OpenError!File { | ||
| 763 | const w = os.windows; | ||
| 764 | const access_mask = w.SYNCHRONIZE | w.GENERIC_WRITE | | ||
| 765 | (if (flags.read) @as(u32, w.GENERIC_READ) else 0); | ||
| 766 | const creation = if (flags.exclusive) | ||
| 767 | @as(u32, w.FILE_CREATE) | ||
| 768 | else if (flags.truncate) | ||
| 769 | @as(u32, w.FILE_OVERWRITE_IF) | ||
| 770 | else | ||
| 771 | @as(u32, w.FILE_OPEN_IF); | ||
| 772 | return self.openFileWindows(sub_path_w, access_mask, creation); | ||
| 773 | } | ||
| 774 | |||
| 775 | /// Deprecated; call `openFile` directly. | ||
| 776 | pub fn openRead(self: Dir, sub_path: []const u8) File.OpenError!File { | ||
| 777 | return self.openFile(sub_path, .{}); | ||
| 778 | } | ||
| 779 | |||
| 780 | /// Deprecated; call `openFileC` directly. | ||
| 781 | pub fn openReadC(self: Dir, sub_path: [*:0]const u8) File.OpenError!File { | ||
| 782 | return self.openFileC(sub_path, .{}); | ||
| 783 | } | ||
| 784 | |||
| 785 | /// Deprecated; call `openFileW` directly. | ||
| 786 | pub fn openReadW(self: Dir, sub_path: [*:0]const u16) File.OpenError!File { | ||
| 787 | return self.openFileW(sub_path, .{}); | ||
| 788 | } | ||
| 789 | |||
| 790 | pub fn openFileWindows( | ||
| 791 | self: Dir, | ||
| 792 | sub_path_w: [*:0]const u16, | ||
| 793 | access_mask: os.windows.ACCESS_MASK, | ||
| 794 | creation: os.windows.ULONG, | ||
| 795 | ) File.OpenError!File { | ||
| 724 | const w = os.windows; | 796 | const w = os.windows; |
| 725 | 797 | ||
| 726 | var result = File{ .handle = undefined }; | 798 | var result = File{ .handle = undefined }; |
| ... | @@ -750,13 +822,13 @@ pub const Dir = struct { | ... | @@ -750,13 +822,13 @@ pub const Dir = struct { |
| 750 | var io: w.IO_STATUS_BLOCK = undefined; | 822 | var io: w.IO_STATUS_BLOCK = undefined; |
| 751 | const rc = w.ntdll.NtCreateFile( | 823 | const rc = w.ntdll.NtCreateFile( |
| 752 | &result.handle, | 824 | &result.handle, |
| 753 | w.GENERIC_READ | w.SYNCHRONIZE, | 825 | access_mask, |
| 754 | &attr, | 826 | &attr, |
| 755 | &io, | 827 | &io, |
| 756 | null, | 828 | null, |
| 757 | w.FILE_ATTRIBUTE_NORMAL, | 829 | w.FILE_ATTRIBUTE_NORMAL, |
| 758 | w.FILE_SHARE_READ, | 830 | w.FILE_SHARE_WRITE | w.FILE_SHARE_READ | w.FILE_SHARE_DELETE, |
| 759 | w.FILE_OPEN, | 831 | creation, |
| 760 | w.FILE_NON_DIRECTORY_FILE | w.FILE_SYNCHRONOUS_IO_NONALERT, | 832 | w.FILE_NON_DIRECTORY_FILE | w.FILE_SYNCHRONOUS_IO_NONALERT, |
| 761 | null, | 833 | null, |
| 762 | 0, | 834 | 0, |
| ... | @@ -771,6 +843,7 @@ pub const Dir = struct { | ... | @@ -771,6 +843,7 @@ pub const Dir = struct { |
| 771 | w.STATUS.ACCESS_DENIED => return error.AccessDenied, | 843 | w.STATUS.ACCESS_DENIED => return error.AccessDenied, |
| 772 | w.STATUS.PIPE_BUSY => return error.PipeBusy, | 844 | w.STATUS.PIPE_BUSY => return error.PipeBusy, |
| 773 | w.STATUS.OBJECT_PATH_SYNTAX_BAD => unreachable, | 845 | w.STATUS.OBJECT_PATH_SYNTAX_BAD => unreachable, |
| 846 | w.STATUS.OBJECT_NAME_COLLISION => return error.PathAlreadyExists, | ||
| 774 | else => return w.unexpectedStatus(rc), | 847 | else => return w.unexpectedStatus(rc), |
| 775 | } | 848 | } |
| 776 | } | 849 | } |
| ... | @@ -790,7 +863,10 @@ pub const Dir = struct { | ... | @@ -790,7 +863,10 @@ pub const Dir = struct { |
| 790 | /// list the contents of a directory, open it with `openDirList`. | 863 | /// list the contents of a directory, open it with `openDirList`. |
| 791 | /// | 864 | /// |
| 792 | /// Call `close` on the result when done. | 865 | /// Call `close` on the result when done. |
| 866 | /// | ||
| 867 | /// Asserts that the path parameter has no null bytes. | ||
| 793 | pub fn openDirTraverse(self: Dir, sub_path: []const u8) OpenError!Dir { | 868 | pub fn openDirTraverse(self: Dir, sub_path: []const u8) OpenError!Dir { |
| 869 | if (std.debug.runtime_safety) for (sub_path) |byte| assert(byte != 0); | ||
| 794 | if (builtin.os == .windows) { | 870 | if (builtin.os == .windows) { |
| 795 | const sub_path_w = try os.windows.sliceToPrefixedFileW(sub_path); | 871 | const sub_path_w = try os.windows.sliceToPrefixedFileW(sub_path); |
| 796 | return self.openDirTraverseW(&sub_path_w); | 872 | return self.openDirTraverseW(&sub_path_w); |
| ... | @@ -805,7 +881,10 @@ pub const Dir = struct { | ... | @@ -805,7 +881,10 @@ pub const Dir = struct { |
| 805 | /// same and may be more efficient. | 881 | /// same and may be more efficient. |
| 806 | /// | 882 | /// |
| 807 | /// Call `close` on the result when done. | 883 | /// Call `close` on the result when done. |
| 884 | /// | ||
| 885 | /// Asserts that the path parameter has no null bytes. | ||
| 808 | pub fn openDirList(self: Dir, sub_path: []const u8) OpenError!Dir { | 886 | pub fn openDirList(self: Dir, sub_path: []const u8) OpenError!Dir { |
| 887 | if (std.debug.runtime_safety) for (sub_path) |byte| assert(byte != 0); | ||
| 809 | if (builtin.os == .windows) { | 888 | if (builtin.os == .windows) { |
| 810 | const sub_path_w = try os.windows.sliceToPrefixedFileW(sub_path); | 889 | const sub_path_w = try os.windows.sliceToPrefixedFileW(sub_path); |
| 811 | return self.openDirListW(&sub_path_w); | 890 | return self.openDirListW(&sub_path_w); |
| ... | @@ -920,9 +999,12 @@ pub const Dir = struct { | ... | @@ -920,9 +999,12 @@ pub const Dir = struct { |
| 920 | pub const DeleteFileError = os.UnlinkError; | 999 | pub const DeleteFileError = os.UnlinkError; |
| 921 | 1000 | ||
| 922 | /// Delete a file name and possibly the file it refers to, based on an open directory handle. | 1001 | /// Delete a file name and possibly the file it refers to, based on an open directory handle. |
| 1002 | /// Asserts that the path parameter has no null bytes. | ||
| 923 | pub fn deleteFile(self: Dir, sub_path: []const u8) DeleteFileError!void { | 1003 | pub fn deleteFile(self: Dir, sub_path: []const u8) DeleteFileError!void { |
| 924 | const sub_path_c = try os.toPosixPath(sub_path); | 1004 | os.unlinkat(self.fd, sub_path, 0) catch |err| switch (err) { |
| 925 | return self.deleteFileC(&sub_path_c); | 1005 | error.DirNotEmpty => unreachable, // not passing AT_REMOVEDIR |
| 1006 | else => |e| return e, | ||
| 1007 | }; | ||
| 926 | } | 1008 | } |
| 927 | 1009 | ||
| 928 | /// Same as `deleteFile` except the parameter is null-terminated. | 1010 | /// Same as `deleteFile` except the parameter is null-terminated. |
| ... | @@ -933,6 +1015,14 @@ pub const Dir = struct { | ... | @@ -933,6 +1015,14 @@ pub const Dir = struct { |
| 933 | }; | 1015 | }; |
| 934 | } | 1016 | } |
| 935 | 1017 | ||
| 1018 | /// Same as `deleteFile` except the parameter is WTF-16 encoded. | ||
| 1019 | pub fn deleteFileW(self: Dir, sub_path_w: [*:0]const u16) DeleteFileError!void { | ||
| 1020 | os.unlinkatW(self.fd, sub_path_w, 0) catch |err| switch (err) { | ||
| 1021 | error.DirNotEmpty => unreachable, // not passing AT_REMOVEDIR | ||
| 1022 | else => |e| return e, | ||
| 1023 | }; | ||
| 1024 | } | ||
| 1025 | |||
| 936 | pub const DeleteDirError = error{ | 1026 | pub const DeleteDirError = error{ |
| 937 | DirNotEmpty, | 1027 | DirNotEmpty, |
| 938 | FileNotFound, | 1028 | FileNotFound, |
| ... | @@ -951,7 +1041,9 @@ pub const Dir = struct { | ... | @@ -951,7 +1041,9 @@ pub const Dir = struct { |
| 951 | 1041 | ||
| 952 | /// Returns `error.DirNotEmpty` if the directory is not empty. | 1042 | /// Returns `error.DirNotEmpty` if the directory is not empty. |
| 953 | /// To delete a directory recursively, see `deleteTree`. | 1043 | /// To delete a directory recursively, see `deleteTree`. |
| 1044 | /// Asserts that the path parameter has no null bytes. | ||
| 954 | pub fn deleteDir(self: Dir, sub_path: []const u8) DeleteDirError!void { | 1045 | pub fn deleteDir(self: Dir, sub_path: []const u8) DeleteDirError!void { |
| 1046 | if (std.debug.runtime_safety) for (sub_path) |byte| assert(byte != 0); | ||
| 955 | if (builtin.os == .windows) { | 1047 | if (builtin.os == .windows) { |
| 956 | const sub_path_w = try os.windows.sliceToPrefixedFileW(sub_path); | 1048 | const sub_path_w = try os.windows.sliceToPrefixedFileW(sub_path); |
| 957 | return self.deleteDirW(&sub_path_w); | 1049 | return self.deleteDirW(&sub_path_w); |
| ... | @@ -979,7 +1071,9 @@ pub const Dir = struct { | ... | @@ -979,7 +1071,9 @@ pub const Dir = struct { |
| 979 | 1071 | ||
| 980 | /// Read value of a symbolic link. | 1072 | /// Read value of a symbolic link. |
| 981 | /// The return value is a slice of `buffer`, from index `0`. | 1073 | /// The return value is a slice of `buffer`, from index `0`. |
| 1074 | /// Asserts that the path parameter has no null bytes. | ||
| 982 | pub fn readLink(self: Dir, sub_path: []const u8, buffer: *[MAX_PATH_BYTES]u8) ![]u8 { | 1075 | pub fn readLink(self: Dir, sub_path: []const u8, buffer: *[MAX_PATH_BYTES]u8) ![]u8 { |
| 1076 | if (std.debug.runtime_safety) for (sub_path) |byte| assert(byte != 0); | ||
| 983 | const sub_path_c = try os.toPosixPath(sub_path); | 1077 | const sub_path_c = try os.toPosixPath(sub_path); |
| 984 | return self.readLinkC(&sub_path_c, buffer); | 1078 | return self.readLinkC(&sub_path_c, buffer); |
| 985 | } | 1079 | } |
| ... | @@ -1190,8 +1284,94 @@ pub const Dir = struct { | ... | @@ -1190,8 +1284,94 @@ pub const Dir = struct { |
| 1190 | } | 1284 | } |
| 1191 | } | 1285 | } |
| 1192 | } | 1286 | } |
| 1287 | |||
| 1288 | /// Writes content to the file system, creating a new file if it does not exist, truncating | ||
| 1289 | /// if it already exists. | ||
| 1290 | pub fn writeFile(self: Dir, sub_path: []const u8, data: []const u8) !void { | ||
| 1291 | var file = try self.createFile(sub_path, .{}); | ||
| 1292 | defer file.close(); | ||
| 1293 | try file.write(data); | ||
| 1294 | } | ||
| 1193 | }; | 1295 | }; |
| 1194 | 1296 | ||
| 1297 | /// Returns an handle to the current working directory that is open for traversal. | ||
| 1298 | /// Closing the returned `Dir` is checked illegal behavior. Iterating over the result is illegal behavior. | ||
| 1299 | /// On POSIX targets, this function is comptime-callable. | ||
| 1300 | pub fn cwd() Dir { | ||
| 1301 | if (builtin.os == .windows) { | ||
| 1302 | return Dir{ .fd = os.windows.peb().ProcessParameters.CurrentDirectory.Handle }; | ||
| 1303 | } else { | ||
| 1304 | return Dir{ .fd = os.AT_FDCWD }; | ||
| 1305 | } | ||
| 1306 | } | ||
| 1307 | |||
| 1308 | /// Opens a file for reading or writing, without attempting to create a new file, based on an absolute path. | ||
| 1309 | /// Call `File.close` to release the resource. | ||
| 1310 | /// Asserts that the path is absolute. See `Dir.openFile` for a function that | ||
| 1311 | /// operates on both absolute and relative paths. | ||
| 1312 | /// Asserts that the path parameter has no null bytes. See `openFileAbsoluteC` for a function | ||
| 1313 | /// that accepts a null-terminated path. | ||
| 1314 | pub fn openFileAbsolute(absolute_path: []const u8, flags: File.OpenFlags) File.OpenError!File { | ||
| 1315 | assert(path.isAbsolute(absolute_path)); | ||
| 1316 | return cwd().openFile(absolute_path, flags); | ||
| 1317 | } | ||
| 1318 | |||
| 1319 | /// Same as `openFileAbsolute` but the path parameter is null-terminated. | ||
| 1320 | pub fn openFileAbsoluteC(absolute_path_c: [*:0]const u8, flags: File.OpenFlags) File.OpenError!File { | ||
| 1321 | assert(path.isAbsoluteC(absolute_path_c)); | ||
| 1322 | return cwd().openFileC(absolute_path_c, flags); | ||
| 1323 | } | ||
| 1324 | |||
| 1325 | /// Same as `openFileAbsolute` but the path parameter is WTF-16 encoded. | ||
| 1326 | pub fn openFileAbsoluteW(absolute_path_w: [*:0]const u16, flags: File.OpenFlags) File.OpenError!File { | ||
| 1327 | assert(path.isAbsoluteW(absolute_path_w)); | ||
| 1328 | return cwd().openFileW(absolute_path_w, flags); | ||
| 1329 | } | ||
| 1330 | |||
| 1331 | /// Creates, opens, or overwrites a file with write access, based on an absolute path. | ||
| 1332 | /// Call `File.close` to release the resource. | ||
| 1333 | /// Asserts that the path is absolute. See `Dir.createFile` for a function that | ||
| 1334 | /// operates on both absolute and relative paths. | ||
| 1335 | /// Asserts that the path parameter has no null bytes. See `createFileAbsoluteC` for a function | ||
| 1336 | /// that accepts a null-terminated path. | ||
| 1337 | pub fn createFileAbsolute(absolute_path: []const u8, flags: File.CreateFlags) File.OpenError!File { | ||
| 1338 | assert(path.isAbsolute(absolute_path)); | ||
| 1339 | return cwd().createFile(absolute_path, flags); | ||
| 1340 | } | ||
| 1341 | |||
| 1342 | /// Same as `createFileAbsolute` but the path parameter is null-terminated. | ||
| 1343 | pub fn createFileAbsoluteC(absolute_path_c: [*:0]const u8, flags: File.CreateFlags) File.OpenError!File { | ||
| 1344 | assert(path.isAbsoluteC(absolute_path_c)); | ||
| 1345 | return cwd().createFileC(absolute_path_c, flags); | ||
| 1346 | } | ||
| 1347 | |||
| 1348 | /// Same as `createFileAbsolute` but the path parameter is WTF-16 encoded. | ||
| 1349 | pub fn createFileAbsoluteW(absolute_path_w: [*:0]const u16, flags: File.CreateFlags) File.OpenError!File { | ||
| 1350 | assert(path.isAbsoluteW(absolute_path_w)); | ||
| 1351 | return cwd().createFileW(absolute_path_w, flags); | ||
| 1352 | } | ||
| 1353 | |||
| 1354 | /// Delete a file name and possibly the file it refers to, based on an absolute path. | ||
| 1355 | /// Asserts that the path is absolute. See `Dir.deleteFile` for a function that | ||
| 1356 | /// operates on both absolute and relative paths. | ||
| 1357 | /// Asserts that the path parameter has no null bytes. | ||
| 1358 | pub fn deleteFileAbsolute(absolute_path: []const u8) DeleteFileError!void { | ||
| 1359 | assert(path.isAbsolute(absolute_path)); | ||
| 1360 | return cwd().deleteFile(absolute_path); | ||
| 1361 | } | ||
| 1362 | |||
| 1363 | /// Same as `deleteFileAbsolute` except the parameter is null-terminated. | ||
| 1364 | pub fn deleteFileAbsoluteC(absolute_path_c: [*:0]const u8) DeleteFileError!void { | ||
| 1365 | assert(path.isAbsoluteC(absolute_path_c)); | ||
| 1366 | return cwd().deleteFileC(absolute_path_c); | ||
| 1367 | } | ||
| 1368 | |||
| 1369 | /// Same as `deleteFileAbsolute` except the parameter is WTF-16 encoded. | ||
| 1370 | pub fn deleteFileAbsoluteW(absolute_path_w: [*:0]const u16) DeleteFileError!void { | ||
| 1371 | assert(path.isAbsoluteW(absolute_path_w)); | ||
| 1372 | return cwd().deleteFileW(absolute_path_w); | ||
| 1373 | } | ||
| 1374 | |||
| 1195 | pub const Walker = struct { | 1375 | pub const Walker = struct { |
| 1196 | stack: std.ArrayList(StackItem), | 1376 | stack: std.ArrayList(StackItem), |
| 1197 | name_buffer: std.Buffer, | 1377 | name_buffer: std.Buffer, |
| ... | @@ -1264,7 +1444,7 @@ pub const Walker = struct { | ... | @@ -1264,7 +1444,7 @@ pub const Walker = struct { |
| 1264 | pub fn walkPath(allocator: *Allocator, dir_path: []const u8) !Walker { | 1444 | pub fn walkPath(allocator: *Allocator, dir_path: []const u8) !Walker { |
| 1265 | assert(!mem.endsWith(u8, dir_path, path.sep_str)); | 1445 | assert(!mem.endsWith(u8, dir_path, path.sep_str)); |
| 1266 | 1446 | ||
| 1267 | var dir = try Dir.cwd().openDirList(dir_path); | 1447 | var dir = try cwd().openDirList(dir_path); |
| 1268 | errdefer dir.close(); | 1448 | errdefer dir.close(); |
| 1269 | 1449 | ||
| 1270 | var name_buffer = try std.Buffer.init(allocator, dir_path); | 1450 | var name_buffer = try std.Buffer.init(allocator, dir_path); |
| ... | @@ -1298,18 +1478,18 @@ pub const OpenSelfExeError = os.OpenError || os.windows.CreateFileError || SelfE | ... | @@ -1298,18 +1478,18 @@ pub const OpenSelfExeError = os.OpenError || os.windows.CreateFileError || SelfE |
| 1298 | 1478 | ||
| 1299 | pub fn openSelfExe() OpenSelfExeError!File { | 1479 | pub fn openSelfExe() OpenSelfExeError!File { |
| 1300 | if (builtin.os == .linux) { | 1480 | if (builtin.os == .linux) { |
| 1301 | return File.openReadC("/proc/self/exe"); | 1481 | return openFileAbsoluteC("/proc/self/exe", .{}); |
| 1302 | } | 1482 | } |
| 1303 | if (builtin.os == .windows) { | 1483 | if (builtin.os == .windows) { |
| 1304 | const wide_slice = selfExePathW(); | 1484 | const wide_slice = selfExePathW(); |
| 1305 | const prefixed_path_w = try os.windows.wToPrefixedFileW(wide_slice); | 1485 | const prefixed_path_w = try os.windows.wToPrefixedFileW(wide_slice); |
| 1306 | return Dir.cwd().openReadW(&prefixed_path_w); | 1486 | return cwd().openReadW(&prefixed_path_w); |
| 1307 | } | 1487 | } |
| 1308 | var buf: [MAX_PATH_BYTES]u8 = undefined; | 1488 | var buf: [MAX_PATH_BYTES]u8 = undefined; |
| 1309 | const self_exe_path = try selfExePath(&buf); | 1489 | const self_exe_path = try selfExePath(&buf); |
| 1310 | buf[self_exe_path.len] = 0; | 1490 | buf[self_exe_path.len] = 0; |
| 1311 | // TODO avoid @ptrCast here using slice syntax with https://github.com/ziglang/zig/issues/3731 | 1491 | // TODO avoid @ptrCast here using slice syntax with https://github.com/ziglang/zig/issues/3770 |
| 1312 | return File.openReadC(@ptrCast([*:0]u8, self_exe_path.ptr)); | 1492 | return openFileAbsoluteC(@ptrCast([*:0]u8, self_exe_path.ptr), .{}); |
| 1313 | } | 1493 | } |
| 1314 | 1494 | ||
| 1315 | test "openSelfExe" { | 1495 | test "openSelfExe" { |
lib/std/fs/file.zig+56-73| ... | @@ -25,105 +25,87 @@ pub const File = struct { | ... | @@ -25,105 +25,87 @@ pub const File = struct { |
| 25 | 25 | ||
| 26 | pub const OpenError = windows.CreateFileError || os.OpenError; | 26 | pub const OpenError = windows.CreateFileError || os.OpenError; |
| 27 | 27 | ||
| 28 | /// Deprecated; call `std.fs.Dir.openRead` directly. | 28 | /// TODO https://github.com/ziglang/zig/issues/3802 |
| 29 | pub const OpenFlags = struct { | ||
| 30 | read: bool = true, | ||
| 31 | write: bool = false, | ||
| 32 | }; | ||
| 33 | |||
| 34 | /// TODO https://github.com/ziglang/zig/issues/3802 | ||
| 35 | pub const CreateFlags = struct { | ||
| 36 | /// Whether the file will be created with read access. | ||
| 37 | read: bool = false, | ||
| 38 | |||
| 39 | /// If the file already exists, and is a regular file, and the access | ||
| 40 | /// mode allows writing, it will be truncated to length 0. | ||
| 41 | truncate: bool = true, | ||
| 42 | |||
| 43 | /// Ensures that this open call creates the file, otherwise causes | ||
| 44 | /// `error.FileAlreadyExists` to be returned. | ||
| 45 | exclusive: bool = false, | ||
| 46 | |||
| 47 | /// For POSIX systems this is the file system mode the file will | ||
| 48 | /// be created with. | ||
| 49 | mode: Mode = default_mode, | ||
| 50 | }; | ||
| 51 | |||
| 52 | /// Deprecated; call `std.fs.Dir.openFile` directly. | ||
| 29 | pub fn openRead(path: []const u8) OpenError!File { | 53 | pub fn openRead(path: []const u8) OpenError!File { |
| 30 | return std.fs.Dir.cwd().openRead(path); | 54 | return std.fs.cwd().openFile(path, .{}); |
| 31 | } | 55 | } |
| 32 | 56 | ||
| 33 | /// Deprecated; call `std.fs.Dir.openReadC` directly. | 57 | /// Deprecated; call `std.fs.Dir.openFileC` directly. |
| 34 | pub fn openReadC(path_c: [*:0]const u8) OpenError!File { | 58 | pub fn openReadC(path_c: [*:0]const u8) OpenError!File { |
| 35 | return std.fs.Dir.cwd().openReadC(path_c); | 59 | return std.fs.cwd().openFileC(path_c, .{}); |
| 36 | } | 60 | } |
| 37 | 61 | ||
| 38 | /// Deprecated; call `std.fs.Dir.openReadW` directly. | 62 | /// Deprecated; call `std.fs.Dir.openFileW` directly. |
| 39 | pub fn openReadW(path_w: [*]const u16) OpenError!File { | 63 | pub fn openReadW(path_w: [*]const u16) OpenError!File { |
| 40 | return std.fs.Dir.cwd().openReadW(path_w); | 64 | return std.fs.cwd().openFileW(path_w, .{}); |
| 41 | } | 65 | } |
| 42 | 66 | ||
| 43 | /// Calls `openWriteMode` with `default_mode` for the mode. | 67 | /// Deprecated; call `std.fs.Dir.createFile` directly. |
| 44 | /// TODO: deprecate this and move it to `std.fs.Dir`. | ||
| 45 | pub fn openWrite(path: []const u8) OpenError!File { | 68 | pub fn openWrite(path: []const u8) OpenError!File { |
| 46 | return openWriteMode(path, default_mode); | 69 | return std.fs.cwd().createFile(path, .{}); |
| 47 | } | 70 | } |
| 48 | 71 | ||
| 49 | /// If the path does not exist it will be created. | 72 | /// Deprecated; call `std.fs.Dir.createFile` directly. |
| 50 | /// If a file already exists in the destination it will be truncated. | ||
| 51 | /// Call close to clean up. | ||
| 52 | /// TODO: deprecate this and move it to `std.fs.Dir`. | ||
| 53 | pub fn openWriteMode(path: []const u8, file_mode: Mode) OpenError!File { | 73 | pub fn openWriteMode(path: []const u8, file_mode: Mode) OpenError!File { |
| 54 | if (builtin.os == .windows) { | 74 | return std.fs.cwd().createFile(path, .{ .mode = file_mode }); |
| 55 | const path_w = try windows.sliceToPrefixedFileW(path); | ||
| 56 | return openWriteModeW(&path_w, file_mode); | ||
| 57 | } | ||
| 58 | const path_c = try os.toPosixPath(path); | ||
| 59 | return openWriteModeC(&path_c, file_mode); | ||
| 60 | } | 75 | } |
| 61 | 76 | ||
| 62 | /// Same as `openWriteMode` except `path` is null-terminated. | 77 | /// Deprecated; call `std.fs.Dir.createFileC` directly. |
| 63 | /// TODO: deprecate this and move it to `std.fs.Dir`. | 78 | pub fn openWriteModeC(path_c: [*:0]const u8, file_mode: Mode) OpenError!File { |
| 64 | pub fn openWriteModeC(path: [*:0]const u8, file_mode: Mode) OpenError!File { | 79 | return std.fs.cwd().createFileC(path_c, .{ .mode = file_mode }); |
| 65 | if (builtin.os == .windows) { | ||
| 66 | const path_w = try windows.cStrToPrefixedFileW(path); | ||
| 67 | return openWriteModeW(&path_w, file_mode); | ||
| 68 | } | ||
| 69 | const O_LARGEFILE = if (@hasDecl(os, "O_LARGEFILE")) os.O_LARGEFILE else 0; | ||
| 70 | const flags = O_LARGEFILE | os.O_WRONLY | os.O_CREAT | os.O_CLOEXEC | os.O_TRUNC; | ||
| 71 | const fd = try os.openC(path, flags, file_mode); | ||
| 72 | return openHandle(fd); | ||
| 73 | } | 80 | } |
| 74 | 81 | ||
| 75 | /// Same as `openWriteMode` except `path` is null-terminated and UTF16LE encoded | 82 | /// Deprecated; call `std.fs.Dir.createFileW` directly. |
| 76 | /// TODO: deprecate this and move it to `std.fs.Dir`. | ||
| 77 | pub fn openWriteModeW(path_w: [*:0]const u16, file_mode: Mode) OpenError!File { | 83 | pub fn openWriteModeW(path_w: [*:0]const u16, file_mode: Mode) OpenError!File { |
| 78 | const handle = try windows.CreateFileW( | 84 | return std.fs.cwd().createFileW(path_w, .{ .mode = file_mode }); |
| 79 | path_w, | ||
| 80 | windows.GENERIC_WRITE, | ||
| 81 | windows.FILE_SHARE_WRITE | windows.FILE_SHARE_READ | windows.FILE_SHARE_DELETE, | ||
| 82 | null, | ||
| 83 | windows.CREATE_ALWAYS, | ||
| 84 | windows.FILE_ATTRIBUTE_NORMAL, | ||
| 85 | null, | ||
| 86 | ); | ||
| 87 | return openHandle(handle); | ||
| 88 | } | 85 | } |
| 89 | 86 | ||
| 90 | /// If the path does not exist it will be created. | 87 | /// Deprecated; call `std.fs.Dir.createFile` directly. |
| 91 | /// If a file already exists in the destination this returns OpenError.PathAlreadyExists | ||
| 92 | /// Call close to clean up. | ||
| 93 | /// TODO: deprecate this and move it to `std.fs.Dir`. | ||
| 94 | pub fn openWriteNoClobber(path: []const u8, file_mode: Mode) OpenError!File { | 88 | pub fn openWriteNoClobber(path: []const u8, file_mode: Mode) OpenError!File { |
| 95 | if (builtin.os == .windows) { | 89 | return std.fs.cwd().createFile(path, .{ |
| 96 | const path_w = try windows.sliceToPrefixedFileW(path); | 90 | .mode = file_mode, |
| 97 | return openWriteNoClobberW(&path_w, file_mode); | 91 | .exclusive = true, |
| 98 | } | 92 | }); |
| 99 | const path_c = try os.toPosixPath(path); | ||
| 100 | return openWriteNoClobberC(&path_c, file_mode); | ||
| 101 | } | 93 | } |
| 102 | 94 | ||
| 103 | /// TODO: deprecate this and move it to `std.fs.Dir`. | 95 | /// Deprecated; call `std.fs.Dir.createFileC` directly. |
| 104 | pub fn openWriteNoClobberC(path: [*:0]const u8, file_mode: Mode) OpenError!File { | 96 | pub fn openWriteNoClobberC(path_c: [*:0]const u8, file_mode: Mode) OpenError!File { |
| 105 | if (builtin.os == .windows) { | 97 | return std.fs.cwd().createFileC(path_c, .{ |
| 106 | const path_w = try windows.cStrToPrefixedFileW(path); | 98 | .mode = file_mode, |
| 107 | return openWriteNoClobberW(&path_w, file_mode); | 99 | .exclusive = true, |
| 108 | } | 100 | }); |
| 109 | const O_LARGEFILE = if (@hasDecl(os, "O_LARGEFILE")) os.O_LARGEFILE else 0; | ||
| 110 | const flags = O_LARGEFILE | os.O_WRONLY | os.O_CREAT | os.O_CLOEXEC | os.O_EXCL; | ||
| 111 | const fd = try os.openC(path, flags, file_mode); | ||
| 112 | return openHandle(fd); | ||
| 113 | } | 101 | } |
| 114 | 102 | ||
| 115 | /// TODO: deprecate this and move it to `std.fs.Dir`. | 103 | /// Deprecated; call `std.fs.Dir.createFileW` directly. |
| 116 | pub fn openWriteNoClobberW(path_w: [*:0]const u16, file_mode: Mode) OpenError!File { | 104 | pub fn openWriteNoClobberW(path_w: [*:0]const u16, file_mode: Mode) OpenError!File { |
| 117 | const handle = try windows.CreateFileW( | 105 | return std.fs.cwd().createFileW(path_w, .{ |
| 118 | path_w, | 106 | .mode = file_mode, |
| 119 | windows.GENERIC_WRITE, | 107 | .exclusive = true, |
| 120 | windows.FILE_SHARE_WRITE | windows.FILE_SHARE_READ | windows.FILE_SHARE_DELETE, | 108 | }); |
| 121 | null, | ||
| 122 | windows.CREATE_NEW, | ||
| 123 | windows.FILE_ATTRIBUTE_NORMAL, | ||
| 124 | null, | ||
| 125 | ); | ||
| 126 | return openHandle(handle); | ||
| 127 | } | 109 | } |
| 128 | 110 | ||
| 129 | pub fn openHandle(handle: os.fd_t) File { | 111 | pub fn openHandle(handle: os.fd_t) File { |
| ... | @@ -246,6 +228,7 @@ pub const File = struct { | ... | @@ -246,6 +228,7 @@ pub const File = struct { |
| 246 | windows.STATUS.SUCCESS => {}, | 228 | windows.STATUS.SUCCESS => {}, |
| 247 | windows.STATUS.BUFFER_OVERFLOW => {}, | 229 | windows.STATUS.BUFFER_OVERFLOW => {}, |
| 248 | windows.STATUS.INVALID_PARAMETER => unreachable, | 230 | windows.STATUS.INVALID_PARAMETER => unreachable, |
| 231 | windows.STATUS.ACCESS_DENIED => return error.AccessDenied, | ||
| 249 | else => return windows.unexpectedStatus(rc), | 232 | else => return windows.unexpectedStatus(rc), |
| 250 | } | 233 | } |
| 251 | return Stat{ | 234 | return Stat{ |
lib/std/fs/path.zig+32-1| ... | @@ -128,6 +128,14 @@ test "join" { | ... | @@ -128,6 +128,14 @@ test "join" { |
| 128 | testJoinPosix([_][]const u8{ "a/", "/c" }, "a/c"); | 128 | testJoinPosix([_][]const u8{ "a/", "/c" }, "a/c"); |
| 129 | } | 129 | } |
| 130 | 130 | ||
| 131 | pub fn isAbsoluteC(path_c: [*:0]const u8) bool { | ||
| 132 | if (builtin.os == .windows) { | ||
| 133 | return isAbsoluteWindowsC(path_c); | ||
| 134 | } else { | ||
| 135 | return isAbsolutePosixC(path_c); | ||
| 136 | } | ||
| 137 | } | ||
| 138 | |||
| 131 | pub fn isAbsolute(path: []const u8) bool { | 139 | pub fn isAbsolute(path: []const u8) bool { |
| 132 | if (builtin.os == .windows) { | 140 | if (builtin.os == .windows) { |
| 133 | return isAbsoluteWindows(path); | 141 | return isAbsoluteWindows(path); |
| ... | @@ -136,7 +144,7 @@ pub fn isAbsolute(path: []const u8) bool { | ... | @@ -136,7 +144,7 @@ pub fn isAbsolute(path: []const u8) bool { |
| 136 | } | 144 | } |
| 137 | } | 145 | } |
| 138 | 146 | ||
| 139 | pub fn isAbsoluteW(path_w: [*]const u16) bool { | 147 | pub fn isAbsoluteW(path_w: [*:0]const u16) bool { |
| 140 | if (path_w[0] == '/') | 148 | if (path_w[0] == '/') |
| 141 | return true; | 149 | return true; |
| 142 | 150 | ||
| ... | @@ -174,10 +182,33 @@ pub fn isAbsoluteWindows(path: []const u8) bool { | ... | @@ -174,10 +182,33 @@ pub fn isAbsoluteWindows(path: []const u8) bool { |
| 174 | return false; | 182 | return false; |
| 175 | } | 183 | } |
| 176 | 184 | ||
| 185 | pub fn isAbsoluteWindowsC(path_c: [*:0]const u8) bool { | ||
| 186 | if (path_c[0] == '/') | ||
| 187 | return true; | ||
| 188 | |||
| 189 | if (path_c[0] == '\\') { | ||
| 190 | return true; | ||
| 191 | } | ||
| 192 | if (path_c[0] == 0 or path_c[1] == 0 or path_c[2] == 0) { | ||
| 193 | return false; | ||
| 194 | } | ||
| 195 | if (path_c[1] == ':') { | ||
| 196 | if (path_c[2] == '/') | ||
| 197 | return true; | ||
| 198 | if (path_c[2] == '\\') | ||
| 199 | return true; | ||
| 200 | } | ||
| 201 | return false; | ||
| 202 | } | ||
| 203 | |||
| 177 | pub fn isAbsolutePosix(path: []const u8) bool { | 204 | pub fn isAbsolutePosix(path: []const u8) bool { |
| 178 | return path[0] == sep_posix; | 205 | return path[0] == sep_posix; |
| 179 | } | 206 | } |
| 180 | 207 | ||
| 208 | pub fn isAbsolutePosixC(path_c: [*:0]const u8) bool { | ||
| 209 | return path_c[0] == sep_posix; | ||
| 210 | } | ||
| 211 | |||
| 181 | test "isAbsoluteWindows" { | 212 | test "isAbsoluteWindows" { |
| 182 | testIsAbsoluteWindows("/", true); | 213 | testIsAbsoluteWindows("/", true); |
| 183 | testIsAbsoluteWindows("//", true); | 214 | testIsAbsoluteWindows("//", true); |
lib/std/io.zig+4-7| ... | @@ -61,17 +61,14 @@ pub const COutStream = @import("io/c_out_stream.zig").COutStream; | ... | @@ -61,17 +61,14 @@ pub const COutStream = @import("io/c_out_stream.zig").COutStream; |
| 61 | pub const InStream = @import("io/in_stream.zig").InStream; | 61 | pub const InStream = @import("io/in_stream.zig").InStream; |
| 62 | pub const OutStream = @import("io/out_stream.zig").OutStream; | 62 | pub const OutStream = @import("io/out_stream.zig").OutStream; |
| 63 | 63 | ||
| 64 | /// TODO move this to `std.fs` and add a version to `std.fs.Dir`. | 64 | /// Deprecated; use `std.fs.Dir.writeFile`. |
| 65 | pub fn writeFile(path: []const u8, data: []const u8) !void { | 65 | pub fn writeFile(path: []const u8, data: []const u8) !void { |
| 66 | var file = try File.openWrite(path); | 66 | return fs.cwd().writeFile(path, data); |
| 67 | defer file.close(); | ||
| 68 | try file.write(data); | ||
| 69 | } | 67 | } |
| 70 | 68 | ||
| 71 | /// On success, caller owns returned buffer. | 69 | /// Deprecated; use `std.fs.Dir.readFileAlloc`. |
| 72 | /// This function is deprecated; use `std.fs.Dir.readFileAlloc`. | ||
| 73 | pub fn readFileAlloc(allocator: *mem.Allocator, path: []const u8) ![]u8 { | 70 | pub fn readFileAlloc(allocator: *mem.Allocator, path: []const u8) ![]u8 { |
| 74 | return fs.Dir.cwd().readFileAlloc(allocator, path, math.maxInt(usize)); | 71 | return fs.cwd().readFileAlloc(allocator, path, math.maxInt(usize)); |
| 75 | } | 72 | } |
| 76 | 73 | ||
| 77 | pub fn BufferedInStream(comptime Error: type) type { | 74 | pub fn BufferedInStream(comptime Error: type) type { |
lib/std/io/test.zig+15-13| ... | @@ -14,12 +14,14 @@ test "write a file, read it, then delete it" { | ... | @@ -14,12 +14,14 @@ test "write a file, read it, then delete it" { |
| 14 | var raw_bytes: [200 * 1024]u8 = undefined; | 14 | var raw_bytes: [200 * 1024]u8 = undefined; |
| 15 | var allocator = &std.heap.FixedBufferAllocator.init(raw_bytes[0..]).allocator; | 15 | var allocator = &std.heap.FixedBufferAllocator.init(raw_bytes[0..]).allocator; |
| 16 | 16 | ||
| 17 | const cwd = fs.cwd(); | ||
| 18 | |||
| 17 | var data: [1024]u8 = undefined; | 19 | var data: [1024]u8 = undefined; |
| 18 | var prng = DefaultPrng.init(1234); | 20 | var prng = DefaultPrng.init(1234); |
| 19 | prng.random.bytes(data[0..]); | 21 | prng.random.bytes(data[0..]); |
| 20 | const tmp_file_name = "temp_test_file.txt"; | 22 | const tmp_file_name = "temp_test_file.txt"; |
| 21 | { | 23 | { |
| 22 | var file = try File.openWrite(tmp_file_name); | 24 | var file = try cwd.createFile(tmp_file_name, .{}); |
| 23 | defer file.close(); | 25 | defer file.close(); |
| 24 | 26 | ||
| 25 | var file_out_stream = file.outStream(); | 27 | var file_out_stream = file.outStream(); |
| ... | @@ -32,8 +34,8 @@ test "write a file, read it, then delete it" { | ... | @@ -32,8 +34,8 @@ test "write a file, read it, then delete it" { |
| 32 | } | 34 | } |
| 33 | 35 | ||
| 34 | { | 36 | { |
| 35 | // make sure openWriteNoClobber doesn't harm the file | 37 | // Make sure the exclusive flag is honored. |
| 36 | if (File.openWriteNoClobber(tmp_file_name, File.default_mode)) |file| { | 38 | if (cwd.createFile(tmp_file_name, .{ .exclusive = true })) |file| { |
| 37 | unreachable; | 39 | unreachable; |
| 38 | } else |err| { | 40 | } else |err| { |
| 39 | std.debug.assert(err == File.OpenError.PathAlreadyExists); | 41 | std.debug.assert(err == File.OpenError.PathAlreadyExists); |
| ... | @@ -41,7 +43,7 @@ test "write a file, read it, then delete it" { | ... | @@ -41,7 +43,7 @@ test "write a file, read it, then delete it" { |
| 41 | } | 43 | } |
| 42 | 44 | ||
| 43 | { | 45 | { |
| 44 | var file = try File.openRead(tmp_file_name); | 46 | var file = try cwd.openFile(tmp_file_name, .{}); |
| 45 | defer file.close(); | 47 | defer file.close(); |
| 46 | 48 | ||
| 47 | const file_size = try file.getEndPos(); | 49 | const file_size = try file.getEndPos(); |
| ... | @@ -58,7 +60,7 @@ test "write a file, read it, then delete it" { | ... | @@ -58,7 +60,7 @@ test "write a file, read it, then delete it" { |
| 58 | expect(mem.eql(u8, contents["begin".len .. contents.len - "end".len], data)); | 60 | expect(mem.eql(u8, contents["begin".len .. contents.len - "end".len], data)); |
| 59 | expect(mem.eql(u8, contents[contents.len - "end".len ..], "end")); | 61 | expect(mem.eql(u8, contents[contents.len - "end".len ..], "end")); |
| 60 | } | 62 | } |
| 61 | try fs.deleteFile(tmp_file_name); | 63 | try cwd.deleteFile(tmp_file_name); |
| 62 | } | 64 | } |
| 63 | 65 | ||
| 64 | test "BufferOutStream" { | 66 | test "BufferOutStream" { |
| ... | @@ -274,7 +276,7 @@ test "BitOutStream" { | ... | @@ -274,7 +276,7 @@ test "BitOutStream" { |
| 274 | test "BitStreams with File Stream" { | 276 | test "BitStreams with File Stream" { |
| 275 | const tmp_file_name = "temp_test_file.txt"; | 277 | const tmp_file_name = "temp_test_file.txt"; |
| 276 | { | 278 | { |
| 277 | var file = try File.openWrite(tmp_file_name); | 279 | var file = try fs.cwd().createFile(tmp_file_name, .{}); |
| 278 | defer file.close(); | 280 | defer file.close(); |
| 279 | 281 | ||
| 280 | var file_out = file.outStream(); | 282 | var file_out = file.outStream(); |
| ... | @@ -291,7 +293,7 @@ test "BitStreams with File Stream" { | ... | @@ -291,7 +293,7 @@ test "BitStreams with File Stream" { |
| 291 | try bit_stream.flushBits(); | 293 | try bit_stream.flushBits(); |
| 292 | } | 294 | } |
| 293 | { | 295 | { |
| 294 | var file = try File.openRead(tmp_file_name); | 296 | var file = try fs.cwd().openFile(tmp_file_name, .{}); |
| 295 | defer file.close(); | 297 | defer file.close(); |
| 296 | 298 | ||
| 297 | var file_in = file.inStream(); | 299 | var file_in = file.inStream(); |
| ... | @@ -316,7 +318,7 @@ test "BitStreams with File Stream" { | ... | @@ -316,7 +318,7 @@ test "BitStreams with File Stream" { |
| 316 | 318 | ||
| 317 | expectError(error.EndOfStream, bit_stream.readBitsNoEof(u1, 1)); | 319 | expectError(error.EndOfStream, bit_stream.readBitsNoEof(u1, 1)); |
| 318 | } | 320 | } |
| 319 | try fs.deleteFile(tmp_file_name); | 321 | try fs.cwd().deleteFile(tmp_file_name); |
| 320 | } | 322 | } |
| 321 | 323 | ||
| 322 | fn testIntSerializerDeserializer(comptime endian: builtin.Endian, comptime packing: io.Packing) !void { | 324 | fn testIntSerializerDeserializer(comptime endian: builtin.Endian, comptime packing: io.Packing) !void { |
| ... | @@ -599,7 +601,7 @@ test "c out stream" { | ... | @@ -599,7 +601,7 @@ test "c out stream" { |
| 599 | const out_file = std.c.fopen(filename, "w") orelse return error.UnableToOpenTestFile; | 601 | const out_file = std.c.fopen(filename, "w") orelse return error.UnableToOpenTestFile; |
| 600 | defer { | 602 | defer { |
| 601 | _ = std.c.fclose(out_file); | 603 | _ = std.c.fclose(out_file); |
| 602 | fs.deleteFileC(filename) catch {}; | 604 | fs.cwd().deleteFileC(filename) catch {}; |
| 603 | } | 605 | } |
| 604 | 606 | ||
| 605 | const out_stream = &io.COutStream.init(out_file).stream; | 607 | const out_stream = &io.COutStream.init(out_file).stream; |
| ... | @@ -608,10 +610,10 @@ test "c out stream" { | ... | @@ -608,10 +610,10 @@ test "c out stream" { |
| 608 | 610 | ||
| 609 | test "File seek ops" { | 611 | test "File seek ops" { |
| 610 | const tmp_file_name = "temp_test_file.txt"; | 612 | const tmp_file_name = "temp_test_file.txt"; |
| 611 | var file = try File.openWrite(tmp_file_name); | 613 | var file = try fs.cwd().createFile(tmp_file_name, .{}); |
| 612 | defer { | 614 | defer { |
| 613 | file.close(); | 615 | file.close(); |
| 614 | fs.deleteFile(tmp_file_name) catch {}; | 616 | fs.cwd().deleteFile(tmp_file_name) catch {}; |
| 615 | } | 617 | } |
| 616 | 618 | ||
| 617 | try file.write([_]u8{0x55} ** 8192); | 619 | try file.write([_]u8{0x55} ** 8192); |
| ... | @@ -632,10 +634,10 @@ test "File seek ops" { | ... | @@ -632,10 +634,10 @@ test "File seek ops" { |
| 632 | 634 | ||
| 633 | test "updateTimes" { | 635 | test "updateTimes" { |
| 634 | const tmp_file_name = "just_a_temporary_file.txt"; | 636 | const tmp_file_name = "just_a_temporary_file.txt"; |
| 635 | var file = try File.openWrite(tmp_file_name); | 637 | var file = try fs.cwd().createFile(tmp_file_name, .{ .read = true }); |
| 636 | defer { | 638 | defer { |
| 637 | file.close(); | 639 | file.close(); |
| 638 | std.fs.deleteFile(tmp_file_name) catch {}; | 640 | std.fs.cwd().deleteFile(tmp_file_name) catch {}; |
| 639 | } | 641 | } |
| 640 | var stat_old = try file.stat(); | 642 | var stat_old = try file.stat(); |
| 641 | // Set atime and mtime to 5s before | 643 | // Set atime and mtime to 5s before |
lib/std/net.zig+2-2| ... | @@ -812,7 +812,7 @@ fn linuxLookupNameFromHosts( | ... | @@ -812,7 +812,7 @@ fn linuxLookupNameFromHosts( |
| 812 | family: os.sa_family_t, | 812 | family: os.sa_family_t, |
| 813 | port: u16, | 813 | port: u16, |
| 814 | ) !void { | 814 | ) !void { |
| 815 | const file = fs.File.openReadC("/etc/hosts") catch |err| switch (err) { | 815 | const file = fs.openFileAbsoluteC("/etc/hosts", .{}) catch |err| switch (err) { |
| 816 | error.FileNotFound, | 816 | error.FileNotFound, |
| 817 | error.NotDir, | 817 | error.NotDir, |
| 818 | error.AccessDenied, | 818 | error.AccessDenied, |
| ... | @@ -1006,7 +1006,7 @@ fn getResolvConf(allocator: *mem.Allocator, rc: *ResolvConf) !void { | ... | @@ -1006,7 +1006,7 @@ fn getResolvConf(allocator: *mem.Allocator, rc: *ResolvConf) !void { |
| 1006 | }; | 1006 | }; |
| 1007 | errdefer rc.deinit(); | 1007 | errdefer rc.deinit(); |
| 1008 | 1008 | ||
| 1009 | const file = fs.File.openReadC("/etc/resolv.conf") catch |err| switch (err) { | 1009 | const file = fs.openFileAbsoluteC("/etc/resolv.conf", .{}) catch |err| switch (err) { |
| 1010 | error.FileNotFound, | 1010 | error.FileNotFound, |
| 1011 | error.NotDir, | 1011 | error.NotDir, |
| 1012 | error.AccessDenied, | 1012 | error.AccessDenied, |
lib/std/os.zig+13-6| ... | @@ -798,7 +798,7 @@ pub fn execvpeC(file: [*:0]const u8, child_argv: [*:null]const ?[*:0]const u8, e | ... | @@ -798,7 +798,7 @@ pub fn execvpeC(file: [*:0]const u8, child_argv: [*:null]const ?[*:0]const u8, e |
| 798 | path_buf[search_path.len] = '/'; | 798 | path_buf[search_path.len] = '/'; |
| 799 | mem.copy(u8, path_buf[search_path.len + 1 ..], file_slice); | 799 | mem.copy(u8, path_buf[search_path.len + 1 ..], file_slice); |
| 800 | path_buf[search_path.len + file_slice.len + 1] = 0; | 800 | path_buf[search_path.len + file_slice.len + 1] = 0; |
| 801 | // TODO avoid @ptrCast here using slice syntax with https://github.com/ziglang/zig/issues/3731 | 801 | // TODO avoid @ptrCast here using slice syntax with https://github.com/ziglang/zig/issues/3770 |
| 802 | err = execveC(@ptrCast([*:0]u8, &path_buf), child_argv, envp); | 802 | err = execveC(@ptrCast([*:0]u8, &path_buf), child_argv, envp); |
| 803 | switch (err) { | 803 | switch (err) { |
| 804 | error.AccessDenied => seen_eacces = true, | 804 | error.AccessDenied => seen_eacces = true, |
| ... | @@ -834,7 +834,7 @@ pub fn execvpe( | ... | @@ -834,7 +834,7 @@ pub fn execvpe( |
| 834 | @memcpy(arg_buf.ptr, arg.ptr, arg.len); | 834 | @memcpy(arg_buf.ptr, arg.ptr, arg.len); |
| 835 | arg_buf[arg.len] = 0; | 835 | arg_buf[arg.len] = 0; |
| 836 | 836 | ||
| 837 | // TODO avoid @ptrCast using slice syntax with https://github.com/ziglang/zig/issues/3731 | 837 | // TODO avoid @ptrCast using slice syntax with https://github.com/ziglang/zig/issues/3770 |
| 838 | argv_buf[i] = @ptrCast([*:0]u8, arg_buf.ptr); | 838 | argv_buf[i] = @ptrCast([*:0]u8, arg_buf.ptr); |
| 839 | } | 839 | } |
| 840 | argv_buf[argv_slice.len] = null; | 840 | argv_buf[argv_slice.len] = null; |
| ... | @@ -842,7 +842,7 @@ pub fn execvpe( | ... | @@ -842,7 +842,7 @@ pub fn execvpe( |
| 842 | const envp_buf = try createNullDelimitedEnvMap(allocator, env_map); | 842 | const envp_buf = try createNullDelimitedEnvMap(allocator, env_map); |
| 843 | defer freeNullDelimitedEnvMap(allocator, envp_buf); | 843 | defer freeNullDelimitedEnvMap(allocator, envp_buf); |
| 844 | 844 | ||
| 845 | // TODO avoid @ptrCast here using slice syntax with https://github.com/ziglang/zig/issues/3731 | 845 | // TODO avoid @ptrCast here using slice syntax with https://github.com/ziglang/zig/issues/3770 |
| 846 | const argv_ptr = @ptrCast([*:null]?[*:0]u8, argv_buf.ptr); | 846 | const argv_ptr = @ptrCast([*:null]?[*:0]u8, argv_buf.ptr); |
| 847 | 847 | ||
| 848 | return execvpeC(argv_buf.ptr[0].?, argv_ptr, envp_buf.ptr); | 848 | return execvpeC(argv_buf.ptr[0].?, argv_ptr, envp_buf.ptr); |
| ... | @@ -863,12 +863,12 @@ pub fn createNullDelimitedEnvMap(allocator: *mem.Allocator, env_map: *const std. | ... | @@ -863,12 +863,12 @@ pub fn createNullDelimitedEnvMap(allocator: *mem.Allocator, env_map: *const std. |
| 863 | @memcpy(env_buf.ptr + pair.key.len + 1, pair.value.ptr, pair.value.len); | 863 | @memcpy(env_buf.ptr + pair.key.len + 1, pair.value.ptr, pair.value.len); |
| 864 | env_buf[env_buf.len - 1] = 0; | 864 | env_buf[env_buf.len - 1] = 0; |
| 865 | 865 | ||
| 866 | // TODO avoid @ptrCast here using slice syntax with https://github.com/ziglang/zig/issues/3731 | 866 | // TODO avoid @ptrCast here using slice syntax with https://github.com/ziglang/zig/issues/3770 |
| 867 | envp_buf[i] = @ptrCast([*:0]u8, env_buf.ptr); | 867 | envp_buf[i] = @ptrCast([*:0]u8, env_buf.ptr); |
| 868 | } | 868 | } |
| 869 | assert(i == envp_count); | 869 | assert(i == envp_count); |
| 870 | } | 870 | } |
| 871 | // TODO avoid @ptrCast here using slice syntax with https://github.com/ziglang/zig/issues/3731 | 871 | // TODO avoid @ptrCast here using slice syntax with https://github.com/ziglang/zig/issues/3770 |
| 872 | assert(envp_buf[envp_count] == null); | 872 | assert(envp_buf[envp_count] == null); |
| 873 | return @ptrCast([*:null]?[*:0]u8, envp_buf.ptr)[0..envp_count]; | 873 | return @ptrCast([*:null]?[*:0]u8, envp_buf.ptr)[0..envp_count]; |
| 874 | } | 874 | } |
| ... | @@ -1087,7 +1087,9 @@ pub const UnlinkatError = UnlinkError || error{ | ... | @@ -1087,7 +1087,9 @@ pub const UnlinkatError = UnlinkError || error{ |
| 1087 | }; | 1087 | }; |
| 1088 | 1088 | ||
| 1089 | /// Delete a file name and possibly the file it refers to, based on an open directory handle. | 1089 | /// Delete a file name and possibly the file it refers to, based on an open directory handle. |
| 1090 | /// Asserts that the path parameter has no null bytes. | ||
| 1090 | pub fn unlinkat(dirfd: fd_t, file_path: []const u8, flags: u32) UnlinkatError!void { | 1091 | pub fn unlinkat(dirfd: fd_t, file_path: []const u8, flags: u32) UnlinkatError!void { |
| 1092 | if (std.debug.runtime_safety) for (file_path) |byte| assert(byte != 0); | ||
| 1091 | if (builtin.os == .windows) { | 1093 | if (builtin.os == .windows) { |
| 1092 | const file_path_w = try windows.sliceToPrefixedFileW(file_path); | 1094 | const file_path_w = try windows.sliceToPrefixedFileW(file_path); |
| 1093 | return unlinkatW(dirfd, &file_path_w, flags); | 1095 | return unlinkatW(dirfd, &file_path_w, flags); |
| ... | @@ -2026,7 +2028,10 @@ pub fn waitpid(pid: i32, flags: u32) u32 { | ... | @@ -2026,7 +2028,10 @@ pub fn waitpid(pid: i32, flags: u32) u32 { |
| 2026 | } | 2028 | } |
| 2027 | } | 2029 | } |
| 2028 | 2030 | ||
| 2029 | pub const FStatError = error{SystemResources} || UnexpectedError; | 2031 | pub const FStatError = error{ |
| 2032 | SystemResources, | ||
| 2033 | AccessDenied, | ||
| 2034 | } || UnexpectedError; | ||
| 2030 | 2035 | ||
| 2031 | pub fn fstat(fd: fd_t) FStatError!Stat { | 2036 | pub fn fstat(fd: fd_t) FStatError!Stat { |
| 2032 | var stat: Stat = undefined; | 2037 | var stat: Stat = undefined; |
| ... | @@ -2036,6 +2041,7 @@ pub fn fstat(fd: fd_t) FStatError!Stat { | ... | @@ -2036,6 +2041,7 @@ pub fn fstat(fd: fd_t) FStatError!Stat { |
| 2036 | EINVAL => unreachable, | 2041 | EINVAL => unreachable, |
| 2037 | EBADF => unreachable, // Always a race condition. | 2042 | EBADF => unreachable, // Always a race condition. |
| 2038 | ENOMEM => return error.SystemResources, | 2043 | ENOMEM => return error.SystemResources, |
| 2044 | EACCES => return error.AccessDenied, | ||
| 2039 | else => |err| return unexpectedErrno(err), | 2045 | else => |err| return unexpectedErrno(err), |
| 2040 | } | 2046 | } |
| 2041 | } | 2047 | } |
| ... | @@ -2045,6 +2051,7 @@ pub fn fstat(fd: fd_t) FStatError!Stat { | ... | @@ -2045,6 +2051,7 @@ pub fn fstat(fd: fd_t) FStatError!Stat { |
| 2045 | EINVAL => unreachable, | 2051 | EINVAL => unreachable, |
| 2046 | EBADF => unreachable, // Always a race condition. | 2052 | EBADF => unreachable, // Always a race condition. |
| 2047 | ENOMEM => return error.SystemResources, | 2053 | ENOMEM => return error.SystemResources, |
| 2054 | EACCES => return error.AccessDenied, | ||
| 2048 | else => |err| return unexpectedErrno(err), | 2055 | else => |err| return unexpectedErrno(err), |
| 2049 | } | 2056 | } |
| 2050 | } | 2057 | } |
lib/std/os/linux/test.zig+3-4| ... | @@ -4,6 +4,7 @@ const linux = std.os.linux; | ... | @@ -4,6 +4,7 @@ const linux = std.os.linux; |
| 4 | const mem = std.mem; | 4 | const mem = std.mem; |
| 5 | const elf = std.elf; | 5 | const elf = std.elf; |
| 6 | const expect = std.testing.expect; | 6 | const expect = std.testing.expect; |
| 7 | const fs = std.fs; | ||
| 7 | 8 | ||
| 8 | test "getpid" { | 9 | test "getpid" { |
| 9 | expect(linux.getpid() != 0); | 10 | expect(linux.getpid() != 0); |
| ... | @@ -45,14 +46,12 @@ test "timer" { | ... | @@ -45,14 +46,12 @@ test "timer" { |
| 45 | err = linux.epoll_wait(@intCast(i32, epoll_fd), @ptrCast([*]linux.epoll_event, &events), 8, -1); | 46 | err = linux.epoll_wait(@intCast(i32, epoll_fd), @ptrCast([*]linux.epoll_event, &events), 8, -1); |
| 46 | } | 47 | } |
| 47 | 48 | ||
| 48 | const File = std.fs.File; | ||
| 49 | |||
| 50 | test "statx" { | 49 | test "statx" { |
| 51 | const tmp_file_name = "just_a_temporary_file.txt"; | 50 | const tmp_file_name = "just_a_temporary_file.txt"; |
| 52 | var file = try File.openWrite(tmp_file_name); | 51 | var file = try fs.cwd().createFile(tmp_file_name, .{}); |
| 53 | defer { | 52 | defer { |
| 54 | file.close(); | 53 | file.close(); |
| 55 | std.fs.deleteFile(tmp_file_name) catch {}; | 54 | fs.cwd().deleteFile(tmp_file_name) catch {}; |
| 56 | } | 55 | } |
| 57 | 56 | ||
| 58 | var statx_buf: linux.Statx = undefined; | 57 | var statx_buf: linux.Statx = undefined; |
lib/std/os/test.zig+2-2| ... | @@ -20,7 +20,7 @@ test "makePath, put some files in it, deleteTree" { | ... | @@ -20,7 +20,7 @@ test "makePath, put some files in it, deleteTree" { |
| 20 | try io.writeFile("os_test_tmp" ++ fs.path.sep_str ++ "b" ++ fs.path.sep_str ++ "c" ++ fs.path.sep_str ++ "file.txt", "nonsense"); | 20 | try io.writeFile("os_test_tmp" ++ fs.path.sep_str ++ "b" ++ fs.path.sep_str ++ "c" ++ fs.path.sep_str ++ "file.txt", "nonsense"); |
| 21 | try io.writeFile("os_test_tmp" ++ fs.path.sep_str ++ "b" ++ fs.path.sep_str ++ "file2.txt", "blah"); | 21 | try io.writeFile("os_test_tmp" ++ fs.path.sep_str ++ "b" ++ fs.path.sep_str ++ "file2.txt", "blah"); |
| 22 | try fs.deleteTree("os_test_tmp"); | 22 | try fs.deleteTree("os_test_tmp"); |
| 23 | if (fs.Dir.cwd().openDirTraverse("os_test_tmp")) |dir| { | 23 | if (fs.cwd().openDirTraverse("os_test_tmp")) |dir| { |
| 24 | @panic("expected error"); | 24 | @panic("expected error"); |
| 25 | } else |err| { | 25 | } else |err| { |
| 26 | expect(err == error.FileNotFound); | 26 | expect(err == error.FileNotFound); |
| ... | @@ -111,7 +111,7 @@ test "AtomicFile" { | ... | @@ -111,7 +111,7 @@ test "AtomicFile" { |
| 111 | const content = try io.readFileAlloc(allocator, test_out_file); | 111 | const content = try io.readFileAlloc(allocator, test_out_file); |
| 112 | expect(mem.eql(u8, content, test_content)); | 112 | expect(mem.eql(u8, content, test_content)); |
| 113 | 113 | ||
| 114 | try fs.deleteFile(test_out_file); | 114 | try fs.cwd().deleteFile(test_out_file); |
| 115 | } | 115 | } |
| 116 | 116 | ||
| 117 | test "thread local storage" { | 117 | test "thread local storage" { |
lib/std/pdb.zig+2-1| ... | @@ -6,6 +6,7 @@ const mem = std.mem; | ... | @@ -6,6 +6,7 @@ const mem = std.mem; |
| 6 | const os = std.os; | 6 | const os = std.os; |
| 7 | const warn = std.debug.warn; | 7 | const warn = std.debug.warn; |
| 8 | const coff = std.coff; | 8 | const coff = std.coff; |
| 9 | const fs = std.fs; | ||
| 9 | const File = std.fs.File; | 10 | const File = std.fs.File; |
| 10 | 11 | ||
| 11 | const ArrayList = std.ArrayList; | 12 | const ArrayList = std.ArrayList; |
| ... | @@ -469,7 +470,7 @@ pub const Pdb = struct { | ... | @@ -469,7 +470,7 @@ pub const Pdb = struct { |
| 469 | msf: Msf, | 470 | msf: Msf, |
| 470 | 471 | ||
| 471 | pub fn openFile(self: *Pdb, coff_ptr: *coff.Coff, file_name: []u8) !void { | 472 | pub fn openFile(self: *Pdb, coff_ptr: *coff.Coff, file_name: []u8) !void { |
| 472 | self.in_file = try File.openRead(file_name); | 473 | self.in_file = try fs.cwd().openFile(file_name, .{}); |
| 473 | self.allocator = coff_ptr.allocator; | 474 | self.allocator = coff_ptr.allocator; |
| 474 | self.coff = coff_ptr; | 475 | self.coff = coff_ptr; |
| 475 | 476 |
src-self-hosted/main.zig+1-1| ... | @@ -702,7 +702,7 @@ async fn fmtPath(fmt: *Fmt, file_path_ref: []const u8, check_mode: bool) FmtErro | ... | @@ -702,7 +702,7 @@ async fn fmtPath(fmt: *Fmt, file_path_ref: []const u8, check_mode: bool) FmtErro |
| 702 | max_src_size, | 702 | max_src_size, |
| 703 | ) catch |err| switch (err) { | 703 | ) catch |err| switch (err) { |
| 704 | error.IsDir, error.AccessDenied => { | 704 | error.IsDir, error.AccessDenied => { |
| 705 | var dir = try fs.Dir.cwd().openDirList(file_path); | 705 | var dir = try fs.cwd().openDirList(file_path); |
| 706 | defer dir.close(); | 706 | defer dir.close(); |
| 707 | 707 | ||
| 708 | var group = event.Group(FmtError!void).init(fmt.allocator); | 708 | var group = event.Group(FmtError!void).init(fmt.allocator); |
src-self-hosted/stage1.zig+1-1| ... | @@ -279,7 +279,7 @@ fn fmtPath(fmt: *Fmt, file_path_ref: []const u8, check_mode: bool) FmtError!void | ... | @@ -279,7 +279,7 @@ fn fmtPath(fmt: *Fmt, file_path_ref: []const u8, check_mode: bool) FmtError!void |
| 279 | const source_code = io.readFileAlloc(fmt.allocator, file_path) catch |err| switch (err) { | 279 | const source_code = io.readFileAlloc(fmt.allocator, file_path) catch |err| switch (err) { |
| 280 | error.IsDir, error.AccessDenied => { | 280 | error.IsDir, error.AccessDenied => { |
| 281 | // TODO make event based (and dir.next()) | 281 | // TODO make event based (and dir.next()) |
| 282 | var dir = try fs.Dir.cwd().openDirList(file_path); | 282 | var dir = try fs.cwd().openDirList(file_path); |
| 283 | defer dir.close(); | 283 | defer dir.close(); |
| 284 | 284 | ||
| 285 | var dir_it = dir.iterate(); | 285 | var dir_it = dir.iterate(); |
test/standalone/cat/main.zig+5-3| ... | @@ -1,7 +1,7 @@ | ... | @@ -1,7 +1,7 @@ |
| 1 | const std = @import("std"); | 1 | const std = @import("std"); |
| 2 | const io = std.io; | 2 | const io = std.io; |
| 3 | const process = std.process; | 3 | const process = std.process; |
| 4 | const File = std.fs.File; | 4 | const fs = std.fs; |
| 5 | const mem = std.mem; | 5 | const mem = std.mem; |
| 6 | const warn = std.debug.warn; | 6 | const warn = std.debug.warn; |
| 7 | const allocator = std.debug.global_allocator; | 7 | const allocator = std.debug.global_allocator; |
| ... | @@ -12,6 +12,8 @@ pub fn main() !void { | ... | @@ -12,6 +12,8 @@ pub fn main() !void { |
| 12 | var catted_anything = false; | 12 | var catted_anything = false; |
| 13 | const stdout_file = io.getStdOut(); | 13 | const stdout_file = io.getStdOut(); |
| 14 | 14 | ||
| 15 | const cwd = fs.cwd(); | ||
| 16 | |||
| 15 | while (args_it.next(allocator)) |arg_or_err| { | 17 | while (args_it.next(allocator)) |arg_or_err| { |
| 16 | const arg = try unwrapArg(arg_or_err); | 18 | const arg = try unwrapArg(arg_or_err); |
| 17 | if (mem.eql(u8, arg, "-")) { | 19 | if (mem.eql(u8, arg, "-")) { |
| ... | @@ -20,7 +22,7 @@ pub fn main() !void { | ... | @@ -20,7 +22,7 @@ pub fn main() !void { |
| 20 | } else if (arg[0] == '-') { | 22 | } else if (arg[0] == '-') { |
| 21 | return usage(exe); | 23 | return usage(exe); |
| 22 | } else { | 24 | } else { |
| 23 | const file = File.openRead(arg) catch |err| { | 25 | const file = cwd.openFile(arg, .{}) catch |err| { |
| 24 | warn("Unable to open file: {}\n", @errorName(err)); | 26 | warn("Unable to open file: {}\n", @errorName(err)); |
| 25 | return err; | 27 | return err; |
| 26 | }; | 28 | }; |
| ... | @@ -40,7 +42,7 @@ fn usage(exe: []const u8) !void { | ... | @@ -40,7 +42,7 @@ fn usage(exe: []const u8) !void { |
| 40 | return error.Invalid; | 42 | return error.Invalid; |
| 41 | } | 43 | } |
| 42 | 44 | ||
| 43 | fn cat_file(stdout: File, file: File) !void { | 45 | fn cat_file(stdout: fs.File, file: fs.File) !void { |
| 44 | var buf: [1024 * 4]u8 = undefined; | 46 | var buf: [1024 * 4]u8 = undefined; |
| 45 | 47 | ||
| 46 | while (true) { | 48 | while (true) { |