| author | |
| committer | |
| log | 916998315967f73c91e682e9ea05dd3232818654 |
| tree | 6cd1ee9b952441c6b6ea1ddbad17a5cfd4b5f860 |
| parent | 877032ec6a0007316f42658d12042f1473de4856 |
10 files changed, 193 insertions(+), 312 deletions(-)
lib/std/Io/Dir.zig+156-17| ... | @@ -1,4 +1,5 @@ | ... | @@ -1,4 +1,5 @@ |
| 1 | const Dir = @This(); | 1 | const Dir = @This(); |
| 2 | const root = @import("root"); | ||
| 2 | 3 | ||
| 3 | const builtin = @import("builtin"); | 4 | const builtin = @import("builtin"); |
| 4 | const native_os = builtin.os.tag; | 5 | const native_os = builtin.os.tag; |
| ... | @@ -11,6 +12,61 @@ const Allocator = std.mem.Allocator; | ... | @@ -11,6 +12,61 @@ const Allocator = std.mem.Allocator; |
| 11 | 12 | ||
| 12 | handle: Handle, | 13 | handle: Handle, |
| 13 | 14 | ||
| 15 | pub const path = std.fs.path; | ||
| 16 | |||
| 17 | /// The maximum length of a file path that the operating system will accept. | ||
| 18 | /// | ||
| 19 | /// Paths, including those returned from file system operations, may be longer | ||
| 20 | /// than this length, but such paths cannot be successfully passed back in | ||
| 21 | /// other file system operations. However, all path components returned by file | ||
| 22 | /// system operations are assumed to fit into a `u8` array of this length. | ||
| 23 | /// | ||
| 24 | /// The byte count includes room for a null sentinel byte. | ||
| 25 | /// | ||
| 26 | /// * On Windows, `[]u8` file paths are encoded as | ||
| 27 | /// [WTF-8](https://wtf-8.codeberg.page/). | ||
| 28 | /// * On WASI, `[]u8` file paths are encoded as valid UTF-8. | ||
| 29 | /// * On other platforms, `[]u8` file paths are opaque sequences of bytes with | ||
| 30 | /// no particular encoding. | ||
| 31 | pub const max_path_bytes = switch (native_os) { | ||
| 32 | .linux, .driverkit, .ios, .maccatalyst, .macos, .tvos, .visionos, .watchos, .freebsd, .openbsd, .netbsd, .dragonfly, .haiku, .illumos, .plan9, .emscripten, .wasi, .serenity => std.posix.PATH_MAX, | ||
| 33 | // Each WTF-16LE code unit may be expanded to 3 WTF-8 bytes. | ||
| 34 | // If it would require 4 WTF-8 bytes, then there would be a surrogate | ||
| 35 | // pair in the WTF-16LE, and we (over)account 3 bytes for it that way. | ||
| 36 | // +1 for the null byte at the end, which can be encoded in 1 byte. | ||
| 37 | .windows => std.os.windows.PATH_MAX_WIDE * 3 + 1, | ||
| 38 | else => if (@hasDecl(root, "os") and @hasDecl(root.os, "PATH_MAX")) | ||
| 39 | root.os.PATH_MAX | ||
| 40 | else | ||
| 41 | @compileError("PATH_MAX not implemented for " ++ @tagName(native_os)), | ||
| 42 | }; | ||
| 43 | |||
| 44 | /// This represents the maximum size of a `[]u8` file name component that | ||
| 45 | /// the platform's common file systems support. File name components returned by file system | ||
| 46 | /// operations are likely to fit into a `u8` array of this length, but | ||
| 47 | /// (depending on the platform) this assumption may not hold for every configuration. | ||
| 48 | /// The byte count does not include a null sentinel byte. | ||
| 49 | /// On Windows, `[]u8` file name components are encoded as [WTF-8](https://wtf-8.codeberg.page/). | ||
| 50 | /// On WASI, file name components are encoded as valid UTF-8. | ||
| 51 | /// On other platforms, `[]u8` components are an opaque sequence of bytes with no particular encoding. | ||
| 52 | pub const max_name_bytes = switch (native_os) { | ||
| 53 | .linux, .driverkit, .ios, .maccatalyst, .macos, .tvos, .visionos, .watchos, .freebsd, .openbsd, .netbsd, .dragonfly, .illumos, .serenity => std.posix.NAME_MAX, | ||
| 54 | // Haiku's NAME_MAX includes the null terminator, so subtract one. | ||
| 55 | .haiku => std.posix.NAME_MAX - 1, | ||
| 56 | // Each WTF-16LE character may be expanded to 3 WTF-8 bytes. | ||
| 57 | // If it would require 4 WTF-8 bytes, then there would be a surrogate | ||
| 58 | // pair in the WTF-16LE, and we (over)account 3 bytes for it that way. | ||
| 59 | .windows => std.os.windows.NAME_MAX * 3, | ||
| 60 | // For WASI, the MAX_NAME will depend on the host OS, so it needs to be | ||
| 61 | // as large as the largest max_name_bytes (Windows) in order to work on any host OS. | ||
| 62 | // TODO determine if this is a reasonable approach | ||
| 63 | .wasi => std.os.windows.NAME_MAX * 3, | ||
| 64 | else => if (@hasDecl(root, "os") and @hasDecl(root.os, "NAME_MAX")) | ||
| 65 | root.os.NAME_MAX | ||
| 66 | else | ||
| 67 | @compileError("NAME_MAX not implemented for " ++ @tagName(native_os)), | ||
| 68 | }; | ||
| 69 | |||
| 14 | pub const Entry = struct { | 70 | pub const Entry = struct { |
| 15 | name: []const u8, | 71 | name: []const u8, |
| 16 | kind: File.Kind, | 72 | kind: File.Kind, |
| ... | @@ -148,7 +204,7 @@ pub const SelectiveWalker = struct { | ... | @@ -148,7 +204,7 @@ pub const SelectiveWalker = struct { |
| 148 | }) |entry| { | 204 | }) |entry| { |
| 149 | self.name_buffer.shrinkRetainingCapacity(dirname_len); | 205 | self.name_buffer.shrinkRetainingCapacity(dirname_len); |
| 150 | if (self.name_buffer.items.len != 0) { | 206 | if (self.name_buffer.items.len != 0) { |
| 151 | try self.name_buffer.append(self.allocator, std.fs.path.sep); | 207 | try self.name_buffer.append(self.allocator, path.sep); |
| 152 | dirname_len += 1; | 208 | dirname_len += 1; |
| 153 | } | 209 | } |
| 154 | try self.name_buffer.ensureUnusedCapacity(self.allocator, entry.name.len + 1); | 210 | try self.name_buffer.ensureUnusedCapacity(self.allocator, entry.name.len + 1); |
| ... | @@ -252,7 +308,7 @@ pub const Walker = struct { | ... | @@ -252,7 +308,7 @@ pub const Walker = struct { |
| 252 | /// Returns 1 for a direct child of the initial directory, 2 for an entry | 308 | /// Returns 1 for a direct child of the initial directory, 2 for an entry |
| 253 | /// within a direct child of the initial directory, etc. | 309 | /// within a direct child of the initial directory, etc. |
| 254 | pub fn depth(self: Walker.Entry) usize { | 310 | pub fn depth(self: Walker.Entry) usize { |
| 255 | return std.mem.countScalar(u8, self.path, std.fs.path.sep) + 1; | 311 | return std.mem.countScalar(u8, self.path, path.sep) + 1; |
| 256 | } | 312 | } |
| 257 | }; | 313 | }; |
| 258 | 314 | ||
| ... | @@ -340,6 +396,11 @@ pub fn access(dir: Dir, io: Io, sub_path: []const u8, options: AccessOptions) Ac | ... | @@ -340,6 +396,11 @@ pub fn access(dir: Dir, io: Io, sub_path: []const u8, options: AccessOptions) Ac |
| 340 | return io.vtable.dirAccess(io.userdata, dir, sub_path, options); | 396 | return io.vtable.dirAccess(io.userdata, dir, sub_path, options); |
| 341 | } | 397 | } |
| 342 | 398 | ||
| 399 | pub fn accessAbsolute(io: Io, absolute_path: []const u8, options: AccessOptions) AccessError!void { | ||
| 400 | assert(path.isAbsolute(absolute_path)); | ||
| 401 | return access(.cwd(), io, absolute_path, options); | ||
| 402 | } | ||
| 403 | |||
| 343 | pub const OpenError = error{ | 404 | pub const OpenError = error{ |
| 344 | FileNotFound, | 405 | FileNotFound, |
| 345 | NotDir, | 406 | NotDir, |
| ... | @@ -379,6 +440,11 @@ pub fn openDir(dir: Dir, io: Io, sub_path: []const u8, options: OpenOptions) Ope | ... | @@ -379,6 +440,11 @@ pub fn openDir(dir: Dir, io: Io, sub_path: []const u8, options: OpenOptions) Ope |
| 379 | return io.vtable.dirOpenDir(io.userdata, dir, sub_path, options); | 440 | return io.vtable.dirOpenDir(io.userdata, dir, sub_path, options); |
| 380 | } | 441 | } |
| 381 | 442 | ||
| 443 | pub fn openDirAbsolute(io: Io, absolute_path: []const u8, options: OpenOptions) OpenError!Dir { | ||
| 444 | assert(path.isAbsolute(absolute_path)); | ||
| 445 | return openDir(.cwd(), io, absolute_path, options); | ||
| 446 | } | ||
| 447 | |||
| 382 | pub fn close(dir: Dir, io: Io) void { | 448 | pub fn close(dir: Dir, io: Io) void { |
| 383 | return io.vtable.dirClose(io.userdata, dir); | 449 | return io.vtable.dirClose(io.userdata, dir); |
| 384 | } | 450 | } |
| ... | @@ -396,6 +462,11 @@ pub fn openFile(dir: Dir, io: Io, sub_path: []const u8, flags: File.OpenFlags) F | ... | @@ -396,6 +462,11 @@ pub fn openFile(dir: Dir, io: Io, sub_path: []const u8, flags: File.OpenFlags) F |
| 396 | return io.vtable.dirOpenFile(io.userdata, dir, sub_path, flags); | 462 | return io.vtable.dirOpenFile(io.userdata, dir, sub_path, flags); |
| 397 | } | 463 | } |
| 398 | 464 | ||
| 465 | pub fn openFileAbsolute(io: Io, absolute_path: []const u8, flags: File.OpenFlags) File.OpenError!File { | ||
| 466 | assert(path.isAbsolute(absolute_path)); | ||
| 467 | return openFile(.cwd(), io, absolute_path, flags); | ||
| 468 | } | ||
| 469 | |||
| 399 | /// Creates, opens, or overwrites a file with write access. | 470 | /// Creates, opens, or overwrites a file with write access. |
| 400 | /// | 471 | /// |
| 401 | /// Allocates a resource to be dellocated with `File.close`. | 472 | /// Allocates a resource to be dellocated with `File.close`. |
| ... | @@ -407,6 +478,10 @@ pub fn createFile(dir: Dir, io: Io, sub_path: []const u8, flags: File.CreateFlag | ... | @@ -407,6 +478,10 @@ pub fn createFile(dir: Dir, io: Io, sub_path: []const u8, flags: File.CreateFlag |
| 407 | return io.vtable.dirCreateFile(io.userdata, dir, sub_path, flags); | 478 | return io.vtable.dirCreateFile(io.userdata, dir, sub_path, flags); |
| 408 | } | 479 | } |
| 409 | 480 | ||
| 481 | pub fn createFileAbsolute(io: Io, absolute_path: []const u8, flags: File.CreateFlags) File.OpenError!File { | ||
| 482 | return createFile(.cwd(), io, absolute_path, flags); | ||
| 483 | } | ||
| 484 | |||
| 410 | pub const WriteFileOptions = struct { | 485 | pub const WriteFileOptions = struct { |
| 411 | /// On Windows, `sub_path` should be encoded as [WTF-8](https://wtf-8.codeberg.page/). | 486 | /// On Windows, `sub_path` should be encoded as [WTF-8](https://wtf-8.codeberg.page/). |
| 412 | /// On WASI, `sub_path` should be encoded as valid UTF-8. | 487 | /// On WASI, `sub_path` should be encoded as valid UTF-8. |
| ... | @@ -476,7 +551,7 @@ pub fn updateFile( | ... | @@ -476,7 +551,7 @@ pub fn updateFile( |
| 476 | } | 551 | } |
| 477 | } | 552 | } |
| 478 | 553 | ||
| 479 | if (std.fs.path.dirname(dest_path)) |dirname| { | 554 | if (path.dirname(dest_path)) |dirname| { |
| 480 | try dest_dir.makePath(io, dirname, .default_dir); | 555 | try dest_dir.makePath(io, dirname, .default_dir); |
| 481 | } | 556 | } |
| 482 | 557 | ||
| ... | @@ -556,6 +631,21 @@ pub fn makeDir(dir: Dir, io: Io, sub_path: []const u8, permissions: Permissions) | ... | @@ -556,6 +631,21 @@ pub fn makeDir(dir: Dir, io: Io, sub_path: []const u8, permissions: Permissions) |
| 556 | return io.vtable.dirMake(io.userdata, dir, sub_path, permissions); | 631 | return io.vtable.dirMake(io.userdata, dir, sub_path, permissions); |
| 557 | } | 632 | } |
| 558 | 633 | ||
| 634 | /// Create a new directory, based on an absolute path. | ||
| 635 | /// | ||
| 636 | /// Asserts that the path is absolute. See `makeDir` for a function that | ||
| 637 | /// operates on both absolute and relative paths. | ||
| 638 | /// | ||
| 639 | /// On Windows, `absolute_path` should be encoded as [WTF-8](https://wtf-8.codeberg.page/). | ||
| 640 | /// On WASI, `absolute_path` should be encoded as valid UTF-8. | ||
| 641 | /// On other platforms, `absolute_path` is an opaque sequence of bytes with no particular encoding. | ||
| 642 | pub fn makeDirAbsolute(io: Io, absolute_path: []const u8, permissions: Permissions) MakeError!void { | ||
| 643 | assert(path.isAbsolute(absolute_path)); | ||
| 644 | return makeDir(.cwd(), io, absolute_path, permissions); | ||
| 645 | } | ||
| 646 | |||
| 647 | test makeDirAbsolute {} | ||
| 648 | |||
| 559 | pub const MakePathError = MakeError || StatPathError; | 649 | pub const MakePathError = MakeError || StatPathError; |
| 560 | 650 | ||
| 561 | /// Creates parent directories with default permissions as necessary to ensure | 651 | /// Creates parent directories with default permissions as necessary to ensure |
| ... | @@ -703,19 +793,20 @@ pub const RealPathAllocError = RealPathError || Allocator.Error; | ... | @@ -703,19 +793,20 @@ pub const RealPathAllocError = RealPathError || Allocator.Error; |
| 703 | 793 | ||
| 704 | /// Same as `realPath` except allocates result. | 794 | /// Same as `realPath` except allocates result. |
| 705 | pub fn realPathAlloc(dir: Dir, io: Io, sub_path: []const u8, allocator: Allocator) RealPathAllocError![:0]u8 { | 795 | pub fn realPathAlloc(dir: Dir, io: Io, sub_path: []const u8, allocator: Allocator) RealPathAllocError![:0]u8 { |
| 706 | var buffer: [std.fs.max_path_bytes]u8 = undefined; | 796 | var buffer: [max_path_bytes]u8 = undefined; |
| 707 | const n = try realPath(dir, io, sub_path, &buffer); | 797 | const n = try realPath(dir, io, sub_path, &buffer); |
| 708 | return allocator.dupeZ(u8, buffer[0..n]); | 798 | return allocator.dupeZ(u8, buffer[0..n]); |
| 709 | } | 799 | } |
| 710 | 800 | ||
| 711 | pub fn realPathAbsolute(io: Io, path: []const u8, out_buffer: []u8) RealPathError!usize { | 801 | pub fn realPathAbsolute(io: Io, absolute_path: []const u8, out_buffer: []u8) RealPathError!usize { |
| 712 | return io.vtable.dirRealPath(io.userdata, .cwd(), path, out_buffer); | 802 | assert(path.isAbsolute(absolute_path)); |
| 803 | return io.vtable.dirRealPath(io.userdata, .cwd(), absolute_path, out_buffer); | ||
| 713 | } | 804 | } |
| 714 | 805 | ||
| 715 | /// Same as `realPathAbsolute` except allocates result. | 806 | /// Same as `realPathAbsolute` except allocates result. |
| 716 | pub fn realPathAbsoluteAlloc(io: Io, path: []const u8, allocator: Allocator) RealPathAllocError![:0]u8 { | 807 | pub fn realPathAbsoluteAlloc(io: Io, absolute_path: []const u8, allocator: Allocator) RealPathAllocError![:0]u8 { |
| 717 | var buffer: [std.fs.max_path_bytes]u8 = undefined; | 808 | var buffer: [max_path_bytes]u8 = undefined; |
| 718 | const n = try realPathAbsolute(io, path, &buffer); | 809 | const n = try realPathAbsolute(io, absolute_path, &buffer); |
| 719 | return allocator.dupeZ(u8, buffer[0..n]); | 810 | return allocator.dupeZ(u8, buffer[0..n]); |
| 720 | } | 811 | } |
| 721 | 812 | ||
| ... | @@ -754,6 +845,13 @@ pub fn deleteFile(dir: Dir, io: Io, sub_path: []const u8) DeleteFileError!void { | ... | @@ -754,6 +845,13 @@ pub fn deleteFile(dir: Dir, io: Io, sub_path: []const u8) DeleteFileError!void { |
| 754 | return io.vtable.dirDeleteFile(io.userdata, dir, sub_path); | 845 | return io.vtable.dirDeleteFile(io.userdata, dir, sub_path); |
| 755 | } | 846 | } |
| 756 | 847 | ||
| 848 | pub fn deleteFileAbsolute(io: Io, absolute_path: []const u8) DeleteFileError!void { | ||
| 849 | assert(path.isAbsolute(absolute_path)); | ||
| 850 | return deleteFile(.cwd(), io, absolute_path); | ||
| 851 | } | ||
| 852 | |||
| 853 | test deleteFileAbsolute {} | ||
| 854 | |||
| 757 | pub const DeleteDirError = error{ | 855 | pub const DeleteDirError = error{ |
| 758 | DirNotEmpty, | 856 | DirNotEmpty, |
| 759 | FileNotFound, | 857 | FileNotFound, |
| ... | @@ -785,6 +883,16 @@ pub fn deleteDir(dir: Dir, io: Io, sub_path: []const u8) DeleteDirError!void { | ... | @@ -785,6 +883,16 @@ pub fn deleteDir(dir: Dir, io: Io, sub_path: []const u8) DeleteDirError!void { |
| 785 | return io.vtable.dirDeleteDir(io.userdata, dir, sub_path); | 883 | return io.vtable.dirDeleteDir(io.userdata, dir, sub_path); |
| 786 | } | 884 | } |
| 787 | 885 | ||
| 886 | /// Same as `deleteDir` except the path is absolute. | ||
| 887 | /// | ||
| 888 | /// On Windows, `dir_path` should be encoded as [WTF-8](https://wtf-8.codeberg.page/). | ||
| 889 | /// On WASI, `dir_path` should be encoded as valid UTF-8. | ||
| 890 | /// On other platforms, `dir_path` is an opaque sequence of bytes with no particular encoding. | ||
| 891 | pub fn deleteDirAbsolute(io: Io, absolute_path: []const u8) DeleteDirError!void { | ||
| 892 | assert(path.isAbsolute(absolute_path)); | ||
| 893 | return deleteDir(.cwd(), io, absolute_path); | ||
| 894 | } | ||
| 895 | |||
| 788 | pub const RenameError = error{ | 896 | pub const RenameError = error{ |
| 789 | /// In WASI, this error may occur when the file descriptor does | 897 | /// In WASI, this error may occur when the file descriptor does |
| 790 | /// not hold the required rights to rename a resource by path relative to it. | 898 | /// not hold the required rights to rename a resource by path relative to it. |
| ... | @@ -893,6 +1001,17 @@ pub fn symLink( | ... | @@ -893,6 +1001,17 @@ pub fn symLink( |
| 893 | return io.vtable.dirSymLink(io.userdata, dir, target_path, sym_link_path, flags); | 1001 | return io.vtable.dirSymLink(io.userdata, dir, target_path, sym_link_path, flags); |
| 894 | } | 1002 | } |
| 895 | 1003 | ||
| 1004 | pub fn symLinkAbsolute( | ||
| 1005 | io: Io, | ||
| 1006 | target_path: []const u8, | ||
| 1007 | sym_link_path: []const u8, | ||
| 1008 | flags: SymLinkFlags, | ||
| 1009 | ) SymLinkError!void { | ||
| 1010 | assert(path.isAbsolute(target_path)); | ||
| 1011 | assert(path.isAbsolute(sym_link_path)); | ||
| 1012 | return symLink(.cwd(), io, target_path, sym_link_path, flags); | ||
| 1013 | } | ||
| 1014 | |||
| 896 | /// Same as `symLink`, except tries to create the symbolic link until it | 1015 | /// Same as `symLink`, except tries to create the symbolic link until it |
| 897 | /// succeeds or encounters an error other than `error.PathAlreadyExists`. | 1016 | /// succeeds or encounters an error other than `error.PathAlreadyExists`. |
| 898 | /// | 1017 | /// |
| ... | @@ -913,15 +1032,15 @@ pub fn symLinkAtomic( | ... | @@ -913,15 +1032,15 @@ pub fn symLinkAtomic( |
| 913 | else => |e| return e, | 1032 | else => |e| return e, |
| 914 | } | 1033 | } |
| 915 | 1034 | ||
| 916 | const dirname = std.fs.path.dirname(sym_link_path) orelse "."; | 1035 | const dirname = path.dirname(sym_link_path) orelse "."; |
| 917 | 1036 | ||
| 918 | const rand_len = @sizeOf(u64) * 2; | 1037 | const rand_len = @sizeOf(u64) * 2; |
| 919 | const temp_path_len = dirname.len + 1 + rand_len; | 1038 | const temp_path_len = dirname.len + 1 + rand_len; |
| 920 | var temp_path_buf: [std.fs.max_path_bytes]u8 = undefined; | 1039 | var temp_path_buf: [max_path_bytes]u8 = undefined; |
| 921 | 1040 | ||
| 922 | if (temp_path_len > temp_path_buf.len) return error.NameTooLong; | 1041 | if (temp_path_len > temp_path_buf.len) return error.NameTooLong; |
| 923 | @memcpy(temp_path_buf[0..dirname.len], dirname); | 1042 | @memcpy(temp_path_buf[0..dirname.len], dirname); |
| 924 | temp_path_buf[dirname.len] = std.fs.path.sep; | 1043 | temp_path_buf[dirname.len] = path.sep; |
| 925 | 1044 | ||
| 926 | const temp_path = temp_path_buf[0..temp_path_len]; | 1045 | const temp_path = temp_path_buf[0..temp_path_len]; |
| 927 | 1046 | ||
| ... | @@ -985,8 +1104,8 @@ pub fn readLink(dir: Dir, io: Io, sub_path: []const u8, buffer: []u8) ReadLinkEr | ... | @@ -985,8 +1104,8 @@ pub fn readLink(dir: Dir, io: Io, sub_path: []const u8, buffer: []u8) ReadLinkEr |
| 985 | /// On Windows, `path` should be encoded as [WTF-8](https://wtf-8.codeberg.page/). | 1104 | /// On Windows, `path` should be encoded as [WTF-8](https://wtf-8.codeberg.page/). |
| 986 | /// On WASI, `path` should be encoded as valid UTF-8. | 1105 | /// On WASI, `path` should be encoded as valid UTF-8. |
| 987 | /// On other platforms, `path` is an opaque sequence of bytes with no particular encoding. | 1106 | /// On other platforms, `path` is an opaque sequence of bytes with no particular encoding. |
| 988 | pub fn readLinkAbsolute(io: Io, path: []const u8, buffer: []u8) ReadLinkError!usize { | 1107 | pub fn readLinkAbsolute(io: Io, absolute_path: []const u8, buffer: []u8) ReadLinkError!usize { |
| 989 | assert(std.fs.path.isAbsolute(path)); | 1108 | assert(path.isAbsolute(absolute_path)); |
| 990 | return io.vtable.dirReadLink(io.userdata, .cwd(), path, buffer); | 1109 | return io.vtable.dirReadLink(io.userdata, .cwd(), path, buffer); |
| 991 | } | 1110 | } |
| 992 | 1111 | ||
| ... | @@ -1298,7 +1417,7 @@ fn deleteTreeMinStackSizeWithKindHint(parent: Dir, io: Io, sub_path: []const u8, | ... | @@ -1298,7 +1417,7 @@ fn deleteTreeMinStackSizeWithKindHint(parent: Dir, io: Io, sub_path: []const u8, |
| 1298 | // Valid use of max_path_bytes because dir_name_buf will only | 1417 | // Valid use of max_path_bytes because dir_name_buf will only |
| 1299 | // ever store a single path component that was returned from the | 1418 | // ever store a single path component that was returned from the |
| 1300 | // filesystem. | 1419 | // filesystem. |
| 1301 | var dir_name_buf: [std.fs.max_path_bytes]u8 = undefined; | 1420 | var dir_name_buf: [max_path_bytes]u8 = undefined; |
| 1302 | var dir_name: []const u8 = sub_path; | 1421 | var dir_name: []const u8 = sub_path; |
| 1303 | 1422 | ||
| 1304 | // Here we must avoid recursion, in order to provide O(1) memory guarantee of this function. | 1423 | // Here we must avoid recursion, in order to provide O(1) memory guarantee of this function. |
| ... | @@ -1521,6 +1640,26 @@ pub fn copyFile( | ... | @@ -1521,6 +1640,26 @@ pub fn copyFile( |
| 1521 | try atomic_file.finish(); | 1640 | try atomic_file.finish(); |
| 1522 | } | 1641 | } |
| 1523 | 1642 | ||
| 1643 | /// Same as `copyFile`, except asserts that both `source_path` and `dest_path` | ||
| 1644 | /// are absolute. | ||
| 1645 | /// | ||
| 1646 | /// On Windows, both paths should be encoded as [WTF-8](https://wtf-8.codeberg.page/). | ||
| 1647 | /// On WASI, both paths should be encoded as valid UTF-8. | ||
| 1648 | /// On other platforms, both paths are an opaque sequence of bytes with no particular encoding. | ||
| 1649 | pub fn copyFileAbsolute( | ||
| 1650 | source_path: []const u8, | ||
| 1651 | dest_path: []const u8, | ||
| 1652 | io: Io, | ||
| 1653 | options: CopyFileOptions, | ||
| 1654 | ) !void { | ||
| 1655 | assert(path.isAbsolute(source_path)); | ||
| 1656 | assert(path.isAbsolute(dest_path)); | ||
| 1657 | const my_cwd = cwd(); | ||
| 1658 | return copyFile(my_cwd, source_path, my_cwd, dest_path, io, options); | ||
| 1659 | } | ||
| 1660 | |||
| 1661 | test copyFileAbsolute {} | ||
| 1662 | |||
| 1524 | pub const AtomicFileOptions = struct { | 1663 | pub const AtomicFileOptions = struct { |
| 1525 | permissions: File.Permissions = .default_file, | 1664 | permissions: File.Permissions = .default_file, |
| 1526 | make_path: bool = false, | 1665 | make_path: bool = false, |
| ... | @@ -1538,13 +1677,13 @@ pub const AtomicFileOptions = struct { | ... | @@ -1538,13 +1677,13 @@ pub const AtomicFileOptions = struct { |
| 1538 | /// On WASI, `dest_path` should be encoded as valid UTF-8. | 1677 | /// On WASI, `dest_path` should be encoded as valid UTF-8. |
| 1539 | /// On other platforms, `dest_path` is an opaque sequence of bytes with no particular encoding. | 1678 | /// On other platforms, `dest_path` is an opaque sequence of bytes with no particular encoding. |
| 1540 | pub fn atomicFile(parent: Dir, io: Io, dest_path: []const u8, options: AtomicFileOptions) !File.Atomic { | 1679 | pub fn atomicFile(parent: Dir, io: Io, dest_path: []const u8, options: AtomicFileOptions) !File.Atomic { |
| 1541 | if (std.fs.path.dirname(dest_path)) |dirname| { | 1680 | if (path.dirname(dest_path)) |dirname| { |
| 1542 | const dir = if (options.make_path) | 1681 | const dir = if (options.make_path) |
| 1543 | try parent.makeOpenPath(io, dirname, .{}) | 1682 | try parent.makeOpenPath(io, dirname, .{}) |
| 1544 | else | 1683 | else |
| 1545 | try parent.openDir(io, dirname, .{}); | 1684 | try parent.openDir(io, dirname, .{}); |
| 1546 | 1685 | ||
| 1547 | return .init(std.fs.path.basename(dest_path), options.permissions, dir, true, options.write_buffer); | 1686 | return .init(path.basename(dest_path), options.permissions, dir, true, options.write_buffer); |
| 1548 | } else { | 1687 | } else { |
| 1549 | return .init(dest_path, options.permissions, parent, false, options.write_buffer); | 1688 | return .init(dest_path, options.permissions, parent, false, options.write_buffer); |
| 1550 | } | 1689 | } |
lib/std/Io/File.zig+6-16| ... | @@ -489,22 +489,6 @@ pub const WriteFileStreamingError = error{ | ... | @@ -489,22 +489,6 @@ pub const WriteFileStreamingError = error{ |
| 489 | SystemResources, | 489 | SystemResources, |
| 490 | } || Io.Cancelable || Io.UnexpectedError; | 490 | } || Io.Cancelable || Io.UnexpectedError; |
| 491 | 491 | ||
| 492 | /// Opens a file for reading or writing, without attempting to create a new | ||
| 493 | /// file, based on an absolute path. | ||
| 494 | /// | ||
| 495 | /// Returns an open resource to be released with `close`. | ||
| 496 | /// | ||
| 497 | /// Asserts that the path is absolute. See `Dir.openFile` for a function that | ||
| 498 | /// operates on both absolute and relative paths. | ||
| 499 | /// | ||
| 500 | /// On Windows, `absolute_path` should be encoded as [WTF-8](https://wtf-8.codeberg.page/). | ||
| 501 | /// On WASI, `absolute_path` should be encoded as valid UTF-8. | ||
| 502 | /// On other platforms, `absolute_path` is an opaque sequence of bytes with no particular encoding. | ||
| 503 | pub fn openAbsolute(io: Io, absolute_path: []const u8, flags: OpenFlags) OpenError!File { | ||
| 504 | assert(std.fs.path.isAbsolute(absolute_path)); | ||
| 505 | return Io.Dir.cwd().openFile(io, absolute_path, flags); | ||
| 506 | } | ||
| 507 | |||
| 508 | pub const SeekError = error{ | 492 | pub const SeekError = error{ |
| 509 | Unseekable, | 493 | Unseekable, |
| 510 | /// The file descriptor does not hold the required rights to seek on it. | 494 | /// The file descriptor does not hold the required rights to seek on it. |
| ... | @@ -579,3 +563,9 @@ pub const DowngradeLockError = Io.Cancelable || Io.UnexpectedError; | ... | @@ -579,3 +563,9 @@ pub const DowngradeLockError = Io.Cancelable || Io.UnexpectedError; |
| 579 | pub fn downgradeLock(file: File, io: Io) LockError!void { | 563 | pub fn downgradeLock(file: File, io: Io) LockError!void { |
| 580 | return io.vtable.fileDowngradeLock(io.userdata, file); | 564 | return io.vtable.fileDowngradeLock(io.userdata, file); |
| 581 | } | 565 | } |
| 566 | |||
| 567 | test { | ||
| 568 | _ = Reader; | ||
| 569 | _ = Writer; | ||
| 570 | _ = Atomic; | ||
| 571 | } |
lib/std/crypto/Certificate/Bundle.zig+1-1| ... | @@ -221,7 +221,7 @@ pub fn addCertsFromFilePathAbsolute( | ... | @@ -221,7 +221,7 @@ pub fn addCertsFromFilePathAbsolute( |
| 221 | now: Io.Timestamp, | 221 | now: Io.Timestamp, |
| 222 | abs_file_path: []const u8, | 222 | abs_file_path: []const u8, |
| 223 | ) AddCertsFromFilePathError!void { | 223 | ) AddCertsFromFilePathError!void { |
| 224 | var file = try fs.openFileAbsolute(abs_file_path, .{}); | 224 | var file = try Io.Dir.openFileAbsolute(io, abs_file_path, .{}); |
| 225 | defer file.close(io); | 225 | defer file.close(io); |
| 226 | var file_reader = file.reader(io, &.{}); | 226 | var file_reader = file.reader(io, &.{}); |
| 227 | return addCertsFromFile(cb, gpa, &file_reader, now.toSeconds()); | 227 | return addCertsFromFile(cb, gpa, &file_reader, now.toSeconds()); |
lib/std/fs.zig+7-259| ... | @@ -1,280 +1,28 @@ | ... | @@ -1,280 +1,28 @@ |
| 1 | //! File System. | 1 | //! File System. |
| 2 | const builtin = @import("builtin"); | ||
| 3 | const native_os = builtin.os.tag; | ||
| 4 | 2 | ||
| 5 | const std = @import("std.zig"); | 3 | const std = @import("std.zig"); |
| 6 | const Io = std.Io; | ||
| 7 | const root = @import("root"); | ||
| 8 | const mem = std.mem; | ||
| 9 | const base64 = std.base64; | ||
| 10 | const crypto = std.crypto; | ||
| 11 | const Allocator = std.mem.Allocator; | ||
| 12 | const assert = std.debug.assert; | ||
| 13 | const posix = std.posix; | ||
| 14 | const windows = std.os.windows; | ||
| 15 | |||
| 16 | const is_darwin = native_os.isDarwin(); | ||
| 17 | |||
| 18 | /// Deprecated. | ||
| 19 | pub const AtomicFile = std.Io.File.Atomic; | ||
| 20 | /// Deprecated. | ||
| 21 | pub const Dir = std.Io.Dir; | ||
| 22 | /// Deprecated. | ||
| 23 | pub const File = std.Io.File; | ||
| 24 | 4 | ||
| 5 | /// Deprecated, use `std.Io.Dir.path`. | ||
| 25 | pub const path = @import("fs/path.zig"); | 6 | pub const path = @import("fs/path.zig"); |
| 26 | pub const wasi = @import("fs/wasi.zig"); | 7 | pub const wasi = @import("fs/wasi.zig"); |
| 27 | 8 | ||
| 28 | pub const getAppDataDir = @import("fs/get_app_data_dir.zig").getAppDataDir; | 9 | pub const getAppDataDir = @import("fs/get_app_data_dir.zig").getAppDataDir; |
| 29 | pub const GetAppDataDirError = @import("fs/get_app_data_dir.zig").GetAppDataDirError; | 10 | pub const GetAppDataDirError = @import("fs/get_app_data_dir.zig").GetAppDataDirError; |
| 30 | 11 | ||
| 31 | /// The maximum length of a file path that the operating system will accept. | ||
| 32 | /// | ||
| 33 | /// Paths, including those returned from file system operations, may be longer | ||
| 34 | /// than this length, but such paths cannot be successfully passed back in | ||
| 35 | /// other file system operations. However, all path components returned by file | ||
| 36 | /// system operations are assumed to fit into a `u8` array of this length. | ||
| 37 | /// | ||
| 38 | /// The byte count includes room for a null sentinel byte. | ||
| 39 | /// | ||
| 40 | /// * On Windows, `[]u8` file paths are encoded as | ||
| 41 | /// [WTF-8](https://wtf-8.codeberg.page/). | ||
| 42 | /// * On WASI, `[]u8` file paths are encoded as valid UTF-8. | ||
| 43 | /// * On other platforms, `[]u8` file paths are opaque sequences of bytes with | ||
| 44 | /// no particular encoding. | ||
| 45 | pub const max_path_bytes = switch (native_os) { | ||
| 46 | .linux, .driverkit, .ios, .maccatalyst, .macos, .tvos, .visionos, .watchos, .freebsd, .openbsd, .netbsd, .dragonfly, .haiku, .illumos, .plan9, .emscripten, .wasi, .serenity => posix.PATH_MAX, | ||
| 47 | // Each WTF-16LE code unit may be expanded to 3 WTF-8 bytes. | ||
| 48 | // If it would require 4 WTF-8 bytes, then there would be a surrogate | ||
| 49 | // pair in the WTF-16LE, and we (over)account 3 bytes for it that way. | ||
| 50 | // +1 for the null byte at the end, which can be encoded in 1 byte. | ||
| 51 | .windows => windows.PATH_MAX_WIDE * 3 + 1, | ||
| 52 | else => if (@hasDecl(root, "os") and @hasDecl(root.os, "PATH_MAX")) | ||
| 53 | root.os.PATH_MAX | ||
| 54 | else | ||
| 55 | @compileError("PATH_MAX not implemented for " ++ @tagName(native_os)), | ||
| 56 | }; | ||
| 57 | |||
| 58 | /// This represents the maximum size of a `[]u8` file name component that | ||
| 59 | /// the platform's common file systems support. File name components returned by file system | ||
| 60 | /// operations are likely to fit into a `u8` array of this length, but | ||
| 61 | /// (depending on the platform) this assumption may not hold for every configuration. | ||
| 62 | /// The byte count does not include a null sentinel byte. | ||
| 63 | /// On Windows, `[]u8` file name components are encoded as [WTF-8](https://wtf-8.codeberg.page/). | ||
| 64 | /// On WASI, file name components are encoded as valid UTF-8. | ||
| 65 | /// On other platforms, `[]u8` components are an opaque sequence of bytes with no particular encoding. | ||
| 66 | pub const max_name_bytes = switch (native_os) { | ||
| 67 | .linux, .driverkit, .ios, .maccatalyst, .macos, .tvos, .visionos, .watchos, .freebsd, .openbsd, .netbsd, .dragonfly, .illumos, .serenity => posix.NAME_MAX, | ||
| 68 | // Haiku's NAME_MAX includes the null terminator, so subtract one. | ||
| 69 | .haiku => posix.NAME_MAX - 1, | ||
| 70 | // Each WTF-16LE character may be expanded to 3 WTF-8 bytes. | ||
| 71 | // If it would require 4 WTF-8 bytes, then there would be a surrogate | ||
| 72 | // pair in the WTF-16LE, and we (over)account 3 bytes for it that way. | ||
| 73 | .windows => windows.NAME_MAX * 3, | ||
| 74 | // For WASI, the MAX_NAME will depend on the host OS, so it needs to be | ||
| 75 | // as large as the largest max_name_bytes (Windows) in order to work on any host OS. | ||
| 76 | // TODO determine if this is a reasonable approach | ||
| 77 | .wasi => windows.NAME_MAX * 3, | ||
| 78 | else => if (@hasDecl(root, "os") and @hasDecl(root.os, "NAME_MAX")) | ||
| 79 | root.os.NAME_MAX | ||
| 80 | else | ||
| 81 | @compileError("NAME_MAX not implemented for " ++ @tagName(native_os)), | ||
| 82 | }; | ||
| 83 | |||
| 84 | pub const base64_alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_".*; | 12 | pub const base64_alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_".*; |
| 85 | 13 | ||
| 86 | /// Base64 encoder, replacing the standard `+/` with `-_` so that it can be used in a file name on any filesystem. | 14 | /// Base64 encoder, replacing the standard `+/` with `-_` so that it can be used in a file name on any filesystem. |
| 87 | pub const base64_encoder = base64.Base64Encoder.init(base64_alphabet, null); | 15 | pub const base64_encoder = std.base64.Base64Encoder.init(base64_alphabet, null); |
| 88 | 16 | ||
| 89 | /// Base64 decoder, replacing the standard `+/` with `-_` so that it can be used in a file name on any filesystem. | 17 | /// Base64 decoder, replacing the standard `+/` with `-_` so that it can be used in a file name on any filesystem. |
| 90 | pub const base64_decoder = base64.Base64Decoder.init(base64_alphabet, null); | 18 | pub const base64_decoder = std.base64.Base64Decoder.init(base64_alphabet, null); |
| 91 | |||
| 92 | /// Same as `Dir.copyFile`, except asserts that both `source_path` and `dest_path` | ||
| 93 | /// are absolute. See `Dir.copyFile` for a function that operates on both | ||
| 94 | /// absolute and relative paths. | ||
| 95 | /// On Windows, both paths should be encoded as [WTF-8](https://wtf-8.codeberg.page/). | ||
| 96 | /// On WASI, both paths should be encoded as valid UTF-8. | ||
| 97 | /// On other platforms, both paths are an opaque sequence of bytes with no particular encoding. | ||
| 98 | pub fn copyFileAbsolute( | ||
| 99 | source_path: []const u8, | ||
| 100 | dest_path: []const u8, | ||
| 101 | args: Dir.CopyFileOptions, | ||
| 102 | ) !void { | ||
| 103 | assert(path.isAbsolute(source_path)); | ||
| 104 | assert(path.isAbsolute(dest_path)); | ||
| 105 | const my_cwd = cwd(); | ||
| 106 | return Dir.copyFile(my_cwd, source_path, my_cwd, dest_path, args); | ||
| 107 | } | ||
| 108 | |||
| 109 | test copyFileAbsolute {} | ||
| 110 | |||
| 111 | /// Create a new directory, based on an absolute path. | ||
| 112 | /// Asserts that the path is absolute. See `Dir.makeDir` for a function that operates | ||
| 113 | /// on both absolute and relative paths. | ||
| 114 | /// On Windows, `absolute_path` should be encoded as [WTF-8](https://wtf-8.codeberg.page/). | ||
| 115 | /// On WASI, `absolute_path` should be encoded as valid UTF-8. | ||
| 116 | /// On other platforms, `absolute_path` is an opaque sequence of bytes with no particular encoding. | ||
| 117 | pub fn makeDirAbsolute(absolute_path: []const u8) !void { | ||
| 118 | assert(path.isAbsolute(absolute_path)); | ||
| 119 | return posix.mkdir(absolute_path, Dir.default_mode); | ||
| 120 | } | ||
| 121 | |||
| 122 | test makeDirAbsolute {} | ||
| 123 | |||
| 124 | /// Same as `makeDirAbsolute` except the parameter is null-terminated. | ||
| 125 | pub fn makeDirAbsoluteZ(absolute_path_z: [*:0]const u8) !void { | ||
| 126 | assert(path.isAbsoluteZ(absolute_path_z)); | ||
| 127 | return posix.mkdirZ(absolute_path_z, Dir.default_mode); | ||
| 128 | } | ||
| 129 | |||
| 130 | test makeDirAbsoluteZ {} | ||
| 131 | |||
| 132 | /// Same as `Dir.deleteDir` except the path is absolute. | ||
| 133 | /// On Windows, `dir_path` should be encoded as [WTF-8](https://wtf-8.codeberg.page/). | ||
| 134 | /// On WASI, `dir_path` should be encoded as valid UTF-8. | ||
| 135 | /// On other platforms, `dir_path` is an opaque sequence of bytes with no particular encoding. | ||
| 136 | pub fn deleteDirAbsolute(dir_path: []const u8) !void { | ||
| 137 | assert(path.isAbsolute(dir_path)); | ||
| 138 | return posix.rmdir(dir_path); | ||
| 139 | } | ||
| 140 | |||
| 141 | /// Same as `deleteDirAbsolute` except the path parameter is null-terminated. | ||
| 142 | pub fn deleteDirAbsoluteZ(dir_path: [*:0]const u8) !void { | ||
| 143 | assert(path.isAbsoluteZ(dir_path)); | ||
| 144 | return posix.rmdirZ(dir_path); | ||
| 145 | } | ||
| 146 | |||
| 147 | /// Deprecated in favor of `Io.Dir.cwd`. | ||
| 148 | pub fn cwd() Io.Dir { | ||
| 149 | return .cwd(); | ||
| 150 | } | ||
| 151 | 19 | ||
| 152 | pub fn defaultWasiCwd() std.os.wasi.fd_t { | 20 | /// Deprecated, use `std.Io.Dir.max_path_bytes`. |
| 153 | // Expect the first preopen to be current working directory. | 21 | pub const max_path_bytes = std.Io.Dir.max_path_bytes; |
| 154 | return 3; | 22 | /// Deprecated, use `std.Io.Dir.max_name_bytes`. |
| 155 | } | 23 | pub const max_name_bytes = std.Io.Dir.max_name_bytes; |
| 156 | |||
| 157 | /// Opens a directory at the given path. The directory is a system resource that remains | ||
| 158 | /// open until `close` is called on the result. | ||
| 159 | /// See `openDirAbsoluteZ` for a function that accepts a null-terminated path. | ||
| 160 | /// | ||
| 161 | /// Asserts that the path parameter has no null bytes. | ||
| 162 | /// On Windows, `absolute_path` should be encoded as [WTF-8](https://wtf-8.codeberg.page/). | ||
| 163 | /// On WASI, `absolute_path` should be encoded as valid UTF-8. | ||
| 164 | /// On other platforms, `absolute_path` is an opaque sequence of bytes with no particular encoding. | ||
| 165 | pub fn openDirAbsolute(absolute_path: []const u8, flags: Dir.OpenOptions) File.OpenError!Dir { | ||
| 166 | assert(path.isAbsolute(absolute_path)); | ||
| 167 | return cwd().openDir(absolute_path, flags); | ||
| 168 | } | ||
| 169 | |||
| 170 | /// Deprecated in favor of `Io.File.openAbsolute`. | ||
| 171 | pub fn openFileAbsolute(absolute_path: []const u8, flags: File.OpenFlags) Io.File.OpenError!Io.File { | ||
| 172 | var threaded: Io.Threaded = .init_single_threaded; | ||
| 173 | const io = threaded.ioBasic(); | ||
| 174 | return Io.File.openAbsolute(io, absolute_path, flags); | ||
| 175 | } | ||
| 176 | |||
| 177 | /// Test accessing `path`. | ||
| 178 | /// Be careful of Time-Of-Check-Time-Of-Use race conditions when using this function. | ||
| 179 | /// For example, instead of testing if a file exists and then opening it, just | ||
| 180 | /// open it and handle the error for file not found. | ||
| 181 | /// See `accessAbsoluteZ` for a function that accepts a null-terminated path. | ||
| 182 | /// On Windows, `absolute_path` should be encoded as [WTF-8](https://wtf-8.codeberg.page/). | ||
| 183 | /// On WASI, `absolute_path` should be encoded as valid UTF-8. | ||
| 184 | /// On other platforms, `absolute_path` is an opaque sequence of bytes with no particular encoding. | ||
| 185 | pub fn accessAbsolute(absolute_path: []const u8, flags: Io.Dir.AccessOptions) Dir.AccessError!void { | ||
| 186 | assert(path.isAbsolute(absolute_path)); | ||
| 187 | try cwd().access(absolute_path, flags); | ||
| 188 | } | ||
| 189 | /// Creates, opens, or overwrites a file with write access, based on an absolute path. | ||
| 190 | /// Call `File.close` to release the resource. | ||
| 191 | /// Asserts that the path is absolute. See `Dir.createFile` for a function that | ||
| 192 | /// operates on both absolute and relative paths. | ||
| 193 | /// Asserts that the path parameter has no null bytes. See `createFileAbsoluteC` for a function | ||
| 194 | /// that accepts a null-terminated path. | ||
| 195 | /// On Windows, `absolute_path` should be encoded as [WTF-8](https://wtf-8.codeberg.page/). | ||
| 196 | /// On WASI, `absolute_path` should be encoded as valid UTF-8. | ||
| 197 | /// On other platforms, `absolute_path` is an opaque sequence of bytes with no particular encoding. | ||
| 198 | pub fn createFileAbsolute(absolute_path: []const u8, flags: File.CreateFlags) File.OpenError!File { | ||
| 199 | assert(path.isAbsolute(absolute_path)); | ||
| 200 | return cwd().createFile(absolute_path, flags); | ||
| 201 | } | ||
| 202 | |||
| 203 | /// Delete a file name and possibly the file it refers to, based on an absolute path. | ||
| 204 | /// Asserts that the path is absolute. See `Dir.deleteFile` for a function that | ||
| 205 | /// operates on both absolute and relative paths. | ||
| 206 | /// Asserts that the path parameter has no null bytes. | ||
| 207 | /// On Windows, `absolute_path` should be encoded as [WTF-8](https://wtf-8.codeberg.page/). | ||
| 208 | /// On WASI, `absolute_path` should be encoded as valid UTF-8. | ||
| 209 | /// On other platforms, `absolute_path` is an opaque sequence of bytes with no particular encoding. | ||
| 210 | pub fn deleteFileAbsolute(absolute_path: []const u8) Dir.DeleteFileError!void { | ||
| 211 | assert(path.isAbsolute(absolute_path)); | ||
| 212 | return cwd().deleteFile(absolute_path); | ||
| 213 | } | ||
| 214 | |||
| 215 | /// Removes a symlink, file, or directory. | ||
| 216 | /// This is equivalent to `Dir.deleteTree` with the base directory. | ||
| 217 | /// Asserts that the path is absolute. See `Dir.deleteTree` for a function that | ||
| 218 | /// operates on both absolute and relative paths. | ||
| 219 | /// Asserts that the path parameter has no null bytes. | ||
| 220 | /// On Windows, `absolute_path` should be encoded as [WTF-8](https://wtf-8.codeberg.page/). | ||
| 221 | /// On WASI, `absolute_path` should be encoded as valid UTF-8. | ||
| 222 | /// On other platforms, `absolute_path` is an opaque sequence of bytes with no particular encoding. | ||
| 223 | pub fn deleteTreeAbsolute(io: Io, absolute_path: []const u8) !void { | ||
| 224 | assert(path.isAbsolute(absolute_path)); | ||
| 225 | const dirname = path.dirname(absolute_path) orelse return error{ | ||
| 226 | /// Attempt to remove the root file system path. | ||
| 227 | /// This error is unreachable if `absolute_path` is relative. | ||
| 228 | CannotDeleteRootDirectory, | ||
| 229 | }.CannotDeleteRootDirectory; | ||
| 230 | |||
| 231 | var dir = try cwd().openDir(dirname, .{}); | ||
| 232 | defer dir.close(io); | ||
| 233 | |||
| 234 | return dir.deleteTree(path.basename(absolute_path)); | ||
| 235 | } | ||
| 236 | |||
| 237 | /// Creates a symbolic link named `sym_link_path` which contains the string `target_path`. | ||
| 238 | /// A symbolic link (also known as a soft link) may point to an existing file or to a nonexistent | ||
| 239 | /// one; the latter case is known as a dangling link. | ||
| 240 | /// If `sym_link_path` exists, it will not be overwritten. | ||
| 241 | /// See also `symLinkAbsoluteZ` and `symLinkAbsoluteW`. | ||
| 242 | /// On Windows, both paths should be encoded as [WTF-8](https://wtf-8.codeberg.page/). | ||
| 243 | /// On WASI, both paths should be encoded as valid UTF-8. | ||
| 244 | /// On other platforms, both paths are an opaque sequence of bytes with no particular encoding. | ||
| 245 | pub fn symLinkAbsolute( | ||
| 246 | target_path: []const u8, | ||
| 247 | sym_link_path: []const u8, | ||
| 248 | flags: Dir.SymLinkFlags, | ||
| 249 | ) !void { | ||
| 250 | assert(path.isAbsolute(target_path)); | ||
| 251 | assert(path.isAbsolute(sym_link_path)); | ||
| 252 | if (native_os == .windows) { | ||
| 253 | const target_path_w = try windows.sliceToPrefixedFileW(null, target_path); | ||
| 254 | const sym_link_path_w = try windows.sliceToPrefixedFileW(null, sym_link_path); | ||
| 255 | return windows.CreateSymbolicLink(null, sym_link_path_w.span(), target_path_w.span(), flags.is_directory); | ||
| 256 | } | ||
| 257 | return posix.symlink(target_path, sym_link_path); | ||
| 258 | } | ||
| 259 | |||
| 260 | /// Windows-only. Same as `symLinkAbsolute` except the parameters are null-terminated, WTF16 LE encoded. | ||
| 261 | /// Note that this function will by default try creating a symbolic link to a file. If you would | ||
| 262 | /// like to create a symbolic link to a directory, specify this with `SymLinkFlags{ .is_directory = true }`. | ||
| 263 | /// See also `symLinkAbsolute`, `symLinkAbsoluteZ`. | ||
| 264 | pub fn symLinkAbsoluteW( | ||
| 265 | target_path_w: [*:0]const u16, | ||
| 266 | sym_link_path_w: [*:0]const u16, | ||
| 267 | flags: Dir.SymLinkFlags, | ||
| 268 | ) !void { | ||
| 269 | assert(path.isAbsoluteWindowsW(target_path_w)); | ||
| 270 | assert(path.isAbsoluteWindowsW(sym_link_path_w)); | ||
| 271 | return windows.CreateSymbolicLink(null, mem.span(sym_link_path_w), mem.span(target_path_w), flags.is_directory); | ||
| 272 | } | ||
| 273 | 24 | ||
| 274 | test { | 25 | test { |
| 275 | _ = AtomicFile; | ||
| 276 | _ = Dir; | ||
| 277 | _ = File; | ||
| 278 | _ = path; | 26 | _ = path; |
| 279 | _ = @import("fs/test.zig"); | 27 | _ = @import("fs/test.zig"); |
| 280 | _ = @import("fs/get_app_data_dir.zig"); | 28 | _ = @import("fs/get_app_data_dir.zig"); |
lib/std/fs/test.zig+14-15| ... | @@ -2087,7 +2087,7 @@ test "'.' and '..' in absolute functions" { | ... | @@ -2087,7 +2087,7 @@ test "'.' and '..' in absolute functions" { |
| 2087 | try fs.copyFileAbsolute(created_file_path, copied_file_path, .{}); | 2087 | try fs.copyFileAbsolute(created_file_path, copied_file_path, .{}); |
| 2088 | const renamed_file_path = try fs.path.join(allocator, &.{ subdir_path, "../rename" }); | 2088 | const renamed_file_path = try fs.path.join(allocator, &.{ subdir_path, "../rename" }); |
| 2089 | try fs.renameAbsolute(copied_file_path, renamed_file_path); | 2089 | try fs.renameAbsolute(copied_file_path, renamed_file_path); |
| 2090 | const renamed_file = try fs.openFileAbsolute(renamed_file_path, .{}); | 2090 | const renamed_file = try Dir.openFileAbsolute(renamed_file_path, .{}); |
| 2091 | renamed_file.close(io); | 2091 | renamed_file.close(io); |
| 2092 | try fs.deleteFileAbsolute(renamed_file_path); | 2092 | try fs.deleteFileAbsolute(renamed_file_path); |
| 2093 | 2093 | ||
| ... | @@ -2202,20 +2202,19 @@ test "invalid UTF-8/WTF-8 paths" { | ... | @@ -2202,20 +2202,19 @@ test "invalid UTF-8/WTF-8 paths" { |
| 2202 | try testing.expectError(expected_err, fs.rename(ctx.dir, invalid_path, ctx.dir, invalid_path)); | 2202 | try testing.expectError(expected_err, fs.rename(ctx.dir, invalid_path, ctx.dir, invalid_path)); |
| 2203 | 2203 | ||
| 2204 | if (native_os != .wasi and ctx.path_type != .relative) { | 2204 | if (native_os != .wasi and ctx.path_type != .relative) { |
| 2205 | try testing.expectError(expected_err, fs.copyFileAbsolute(invalid_path, invalid_path, .{})); | 2205 | try testing.expectError(expected_err, Dir.copyFileAbsolute(invalid_path, invalid_path, .{})); |
| 2206 | try testing.expectError(expected_err, fs.makeDirAbsolute(invalid_path)); | 2206 | try testing.expectError(expected_err, Dir.makeDirAbsolute(invalid_path)); |
| 2207 | try testing.expectError(expected_err, fs.deleteDirAbsolute(invalid_path)); | 2207 | try testing.expectError(expected_err, Dir.deleteDirAbsolute(invalid_path)); |
| 2208 | try testing.expectError(expected_err, fs.renameAbsolute(invalid_path, invalid_path)); | 2208 | try testing.expectError(expected_err, Dir.renameAbsolute(invalid_path, invalid_path)); |
| 2209 | try testing.expectError(expected_err, fs.openDirAbsolute(invalid_path, .{})); | 2209 | try testing.expectError(expected_err, Dir.openDirAbsolute(invalid_path, .{})); |
| 2210 | try testing.expectError(expected_err, fs.openFileAbsolute(invalid_path, .{})); | 2210 | try testing.expectError(expected_err, Dir.openFileAbsolute(invalid_path, .{})); |
| 2211 | try testing.expectError(expected_err, fs.accessAbsolute(invalid_path, .{})); | 2211 | try testing.expectError(expected_err, Dir.accessAbsolute(invalid_path, .{})); |
| 2212 | try testing.expectError(expected_err, fs.createFileAbsolute(invalid_path, .{})); | 2212 | try testing.expectError(expected_err, Dir.createFileAbsolute(invalid_path, .{})); |
| 2213 | try testing.expectError(expected_err, fs.deleteFileAbsolute(invalid_path)); | 2213 | try testing.expectError(expected_err, Dir.deleteFileAbsolute(invalid_path)); |
| 2214 | try testing.expectError(expected_err, fs.deleteTreeAbsolute(invalid_path)); | 2214 | var readlink_buf: [Dir.max_path_bytes]u8 = undefined; |
| 2215 | var readlink_buf: [fs.max_path_bytes]u8 = undefined; | 2215 | try testing.expectError(expected_err, Dir.readLinkAbsolute(invalid_path, &readlink_buf)); |
| 2216 | try testing.expectError(expected_err, fs.readLinkAbsolute(invalid_path, &readlink_buf)); | 2216 | try testing.expectError(expected_err, Dir.symLinkAbsolute(invalid_path, invalid_path, .{})); |
| 2217 | try testing.expectError(expected_err, fs.symLinkAbsolute(invalid_path, invalid_path, .{})); | 2217 | try testing.expectError(expected_err, Dir.realpathAlloc(testing.allocator, invalid_path)); |
| 2218 | try testing.expectError(expected_err, fs.realpathAlloc(testing.allocator, invalid_path)); | ||
| 2219 | } | 2218 | } |
| 2220 | } | 2219 | } |
| 2221 | }.impl); | 2220 | }.impl); |
lib/std/os.zig+5| ... | @@ -72,3 +72,8 @@ pub fn fstat_wasi(fd: posix.fd_t) FstatError!wasi.filestat_t { | ... | @@ -72,3 +72,8 @@ pub fn fstat_wasi(fd: posix.fd_t) FstatError!wasi.filestat_t { |
| 72 | else => |err| return posix.unexpectedErrno(err), | 72 | else => |err| return posix.unexpectedErrno(err), |
| 73 | } | 73 | } |
| 74 | } | 74 | } |
| 75 | |||
| 76 | pub fn defaultWasiCwd() std.os.wasi.fd_t { | ||
| 77 | // Expect the first preopen to be current working directory. | ||
| 78 | return 3; | ||
| 79 | } |
lib/std/process.zig+1-1| ... | @@ -1576,7 +1576,7 @@ pub fn getUserInfo(name: []const u8) !UserInfo { | ... | @@ -1576,7 +1576,7 @@ pub fn getUserInfo(name: []const u8) !UserInfo { |
| 1576 | /// TODO this reads /etc/passwd. But sometimes the user/id mapping is in something else | 1576 | /// TODO this reads /etc/passwd. But sometimes the user/id mapping is in something else |
| 1577 | /// like NIS, AD, etc. See `man nss` or look at an strace for `id myuser`. | 1577 | /// like NIS, AD, etc. See `man nss` or look at an strace for `id myuser`. |
| 1578 | pub fn posixGetUserInfo(io: Io, name: []const u8) !UserInfo { | 1578 | pub fn posixGetUserInfo(io: Io, name: []const u8) !UserInfo { |
| 1579 | const file = try std.fs.openFileAbsolute("/etc/passwd", .{}); | 1579 | const file = try Io.Dir.openFileAbsolute(io, "/etc/passwd", .{}); |
| 1580 | defer file.close(io); | 1580 | defer file.close(io); |
| 1581 | var buffer: [4096]u8 = undefined; | 1581 | var buffer: [4096]u8 = undefined; |
| 1582 | var file_reader = file.reader(&buffer); | 1582 | var file_reader = file.reader(&buffer); |
lib/std/std.zig+1-1| ... | @@ -115,7 +115,7 @@ pub const Options = struct { | ... | @@ -115,7 +115,7 @@ pub const Options = struct { |
| 115 | enable_segfault_handler: bool = debug.default_enable_segfault_handler, | 115 | enable_segfault_handler: bool = debug.default_enable_segfault_handler, |
| 116 | 116 | ||
| 117 | /// Function used to implement `std.fs.cwd` for WASI. | 117 | /// Function used to implement `std.fs.cwd` for WASI. |
| 118 | wasiCwd: fn () os.wasi.fd_t = fs.defaultWasiCwd, | 118 | wasiCwd: fn () os.wasi.fd_t = os.defaultWasiCwd, |
| 119 | 119 | ||
| 120 | /// The current log level. | 120 | /// The current log level. |
| 121 | log_level: log.Level = log.default_level, | 121 | log_level: log.Level = log.default_level, |
lib/std/zig/system.zig+1-1| ... | @@ -1024,7 +1024,7 @@ fn detectAbiAndDynamicLinker(io: Io, cpu: Target.Cpu, os: Target.Os, query: Targ | ... | @@ -1024,7 +1024,7 @@ fn detectAbiAndDynamicLinker(io: Io, cpu: Target.Cpu, os: Target.Os, query: Targ |
| 1024 | }; | 1024 | }; |
| 1025 | 1025 | ||
| 1026 | while (true) { | 1026 | while (true) { |
| 1027 | const file = fs.openFileAbsolute(file_name, .{}) catch |err| switch (err) { | 1027 | const file = Io.Dir.openFileAbsolute(io, file_name, .{}) catch |err| switch (err) { |
| 1028 | error.NoSpaceLeft => return error.Unexpected, | 1028 | error.NoSpaceLeft => return error.Unexpected, |
| 1029 | error.NameTooLong => return error.Unexpected, | 1029 | error.NameTooLong => return error.Unexpected, |
| 1030 | error.PathAlreadyExists => return error.Unexpected, | 1030 | error.PathAlreadyExists => return error.Unexpected, |
lib/std/zig/system/linux.zig+1-1| ... | @@ -444,7 +444,7 @@ inline fn getAArch64CpuFeature(comptime feat_reg: []const u8) u64 { | ... | @@ -444,7 +444,7 @@ inline fn getAArch64CpuFeature(comptime feat_reg: []const u8) u64 { |
| 444 | } | 444 | } |
| 445 | 445 | ||
| 446 | pub fn detectNativeCpuAndFeatures(io: Io) ?Target.Cpu { | 446 | pub fn detectNativeCpuAndFeatures(io: Io) ?Target.Cpu { |
| 447 | var file = fs.openFileAbsolute("/proc/cpuinfo", .{}) catch |err| switch (err) { | 447 | var file = Io.Dir.openFileAbsolute(io, "/proc/cpuinfo", .{}) catch |err| switch (err) { |
| 448 | else => return null, | 448 | else => return null, |
| 449 | }; | 449 | }; |
| 450 | defer file.close(io); | 450 | defer file.close(io); |