Commit aa6fcaf76f

Jakub Konka <kubkon@jakubkonka.com>
2020-07-21 20:40:34
Add missing cross-platform Dir.readLink fns
1 parent 65581b3
Changed files (3)
lib/std/fs/test.zig
@@ -10,6 +10,32 @@ const Dir = std.fs.Dir;
 const File = std.fs.File;
 const tmpDir = testing.tmpDir;
 
+test "Dir.readLink" {
+    var tmp = tmpDir(.{});
+    defer tmp.cleanup();
+
+    // Create some targets
+    try tmp.dir.writeFile("file.txt", "nonsense");
+    try tmp.dir.makeDir("subdir");
+
+    {
+        // Create symbolic link by path
+        try tmp.dir.symLink("file.txt", "symlink1", .{});
+        try testReadLink(tmp.dir, "file.txt", "symlink1");
+    }
+    {
+        // Create symbolic link by path
+        try tmp.dir.symLink("subdir", "symlink2", .{ .is_directory = true });
+        try testReadLink(tmp.dir, "subdir", "symlink2");
+    }
+}
+
+fn testReadLink(dir: Dir, target_path: []const u8, symlink_path: []const u8) !void {
+    var buffer: [fs.MAX_PATH_BYTES]u8 = undefined;
+    const given = try dir.readLink(symlink_path, buffer[0..]);
+    testing.expect(mem.eql(u8, target_path, given));
+}
+
 test "readLinkAbsolute" {
     if (builtin.os.tag == .wasi) return error.SkipZigTest;
 
lib/std/fs.zig
@@ -1215,7 +1215,6 @@ pub const Dir = struct {
     /// A symbolic link (also known as a soft link) may point to an existing file or to a nonexistent
     /// one; the latter case is known as a dangling link.
     /// If `sym_link_path` exists, it will not be overwritten.
-    /// TODO add Windows support
     pub fn symLink(
         self: Dir,
         target_path: []const u8,
@@ -1275,17 +1274,38 @@ pub const Dir = struct {
     /// The return value is a slice of `buffer`, from index `0`.
     /// Asserts that the path parameter has no null bytes.
     pub fn readLink(self: Dir, sub_path: []const u8, buffer: []u8) ![]u8 {
+        if (builtin.os.tag == .wasi) {
+            return self.readLinkWasi(sub_path, buffer);
+        }
+        if (builtin.os.tag == .windows) {
+            return os.windows.ReadLink(self.fd, sub_path, buffer);
+        }
         const sub_path_c = try os.toPosixPath(sub_path);
         return self.readLinkZ(&sub_path_c, buffer);
     }
 
     pub const readLinkC = @compileError("deprecated: renamed to readLinkZ");
 
+    /// WASI-only. Same as `readLink` except targeting WASI.
+    pub fn readLinkWasi(self: Dir, sub_path: []const u8, buffer: []u8) ![]u8 {
+        return os.readlinkatWasi(self.fd, sub_path, buffer);
+    }
+
     /// Same as `readLink`, except the `pathname` parameter is null-terminated.
     pub fn readLinkZ(self: Dir, sub_path_c: [*:0]const u8, buffer: []u8) ![]u8 {
+        if (builtin.os.tag == .windows) {
+            const sub_path_w = try os.windows.cStrToPrefixedFileW(sub_path_c);
+            return self.readLinkW(sub_path_w, buffer);
+        }
         return os.readlinkatZ(self.fd, sub_path_c, buffer);
     }
 
+    /// Windows-only. Same as `readLink` except the pathname parameter
+    /// is null-terminated, WTF16 encoded.
+    pub fn readLinkW(self: Dir, sub_path_w: [*:0]const u16, buffer: []u8) ![]u8 {
+        return os.windows.ReadLinkW(self.fd, sub_path_w, buffer);
+    }
+
     /// On success, caller owns returned buffer.
     /// If the file is larger than `max_bytes`, returns `error.FileTooBig`.
     pub fn readFileAlloc(self: Dir, allocator: *mem.Allocator, file_path: []const u8, max_bytes: usize) ![]u8 {
lib/std/os.zig
@@ -1594,9 +1594,7 @@ pub fn symlinkat(target_path: []const u8, newdirfd: fd_t, sym_link_path: []const
         return symlinkatWasi(target_path, newdirfd, sym_link_path);
     }
     if (builtin.os.tag == .windows) {
-        const target_path_w = try windows.sliceToPrefixedFileW(target_path);
-        const sym_link_path_w = try windows.sliceToPrefixedFileW(sym_link_path);
-        return symlinkatW(target_path_w.span().ptr, newdirfd, sym_link_path_w.span().ptr);
+        @compileError("symlinkat is not supported on Windows; use std.os.windows.CreateSymbolicLink instead");
     }
     const target_path_c = try toPosixPath(target_path);
     const sym_link_path_c = try toPosixPath(sym_link_path);
@@ -1629,19 +1627,11 @@ pub fn symlinkatWasi(target_path: []const u8, newdirfd: fd_t, sym_link_path: []c
     }
 }
 
-/// Windows-only. The same as `symlinkat` except the paths are null-terminated, WTF-16 encoded.
-/// See also `symlinkat`.
-pub fn symlinkatW(target_path: [*:0]const u16, newdirfd: fd_t, sym_link_path: [*:0]const u16) SymLinkError!void {
-    @compileError("TODO implement on Windows");
-}
-
 /// The same as `symlinkat` except the parameters are null-terminated pointers.
 /// See also `symlinkat`.
 pub fn symlinkatZ(target_path: [*:0]const u8, newdirfd: fd_t, sym_link_path: [*:0]const u8) SymLinkError!void {
     if (builtin.os.tag == .windows) {
-        const target_path_w = try windows.cStrToPrefixedFileW(target_path);
-        const sym_link_path_w = try windows.cStrToPrefixedFileW(sym_link_path);
-        return symlinkatW(target_path_w.span().ptr, newdirfd, sym_link_path.span().ptr);
+        @compileError("symlinkat is not supported on Windows; use std.os.windows.CreateSymbolicLink instead");
     }
     switch (errno(system.symlinkat(target_path, newdirfd, sym_link_path))) {
         0 => return,