authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2020-03-19 14:38:21-04:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2020-03-19 14:43:41-04:00
log555a2c03286507ffe4bd3bea2154dbfb719ebef1
tree3c0c894c6cbf165aefb17f4561314cb83b3a0d2c
parente3c92d05328d7b40927bed66e7c2500a7853cdc8
signaturelock-open Commit is signed but in an unrecognized format.

(breaking) std.fs.copyFile now integrates with Dir

Removed: * `std.fs.updateFile` * `std.fs.updateFileMode` * `std.fs.copyFile` * `std.fs.copyFileMode` Added: * `std.fs.Dir.copyFile` * `std.fs.copyFileAbsolute` * `std.fs.updateFileAbsolute` Moved: * `std.fs.Dir.UpdateFileOptions` => `std.fs.CopyFileOptions` Deprecated: * `std.fs.deleteDir` * `std.fs.deleteDirC` * `std.fs.deleteDirW` * `std.fs.readLink` * `std.fs.readLinkC`

3 files changed, 71 insertions(+), 61 deletions(-)

lib/std/build.zig+2-1
...@@ -847,7 +847,8 @@ pub const Builder = struct {...@@ -847,7 +847,8 @@ pub const Builder = struct {
847 if (self.verbose) {847 if (self.verbose) {
848 warn("cp {} {} ", .{ source_path, dest_path });848 warn("cp {} {} ", .{ source_path, dest_path });
849 }849 }
850 const prev_status = try fs.updateFile(source_path, dest_path);850 const cwd = fs.cwd();
851 const prev_status = try fs.Dir.updateFile(cwd, source_path, cwd, dest_path, .{});
851 if (self.verbose) switch (prev_status) {852 if (self.verbose) switch (prev_status) {
852 .stale => warn("# installed\n", .{}),853 .stale => warn("# installed\n", .{}),
853 .fresh => warn("# up-to-date\n", .{}),854 .fresh => warn("# up-to-date\n", .{}),
lib/std/fs.zig+61-54
...@@ -86,51 +86,33 @@ pub const PrevStatus = enum {...@@ -86,51 +86,33 @@ pub const PrevStatus = enum {
86 fresh,86 fresh,
87};87};
8888
89/// Deprecated; use `Dir.updateFile`.89pub const CopyFileOptions = struct {
90pub fn updateFile(source_path: []const u8, dest_path: []const u8) !PrevStatus {90 /// When this is `null` the mode is copied from the source file.
91 const my_cwd = cwd();91 override_mode: ?File.Mode = null,
92 return Dir.updateFile(my_cwd, source_path, my_cwd, dest_path, .{});92};
93}
9493
95/// Deprecated; use `Dir.updateFile`.94/// Same as `Dir.updateFile`, except asserts that both `source_path` and `dest_path`
96pub fn updateFileMode(source_path: []const u8, dest_path: []const u8, mode: ?File.Mode) !Dir.PrevStatus {95/// are absolute. See `Dir.updateFile` for a function that operates on both
96/// absolute and relative paths.
97pub fn updateFileAbsolute(
98 source_path: []const u8,
99 dest_path: []const u8,
100 args: CopyFileOptions,
101) !PrevStatus {
102 assert(path.isAbsolute(source_path));
103 assert(path.isAbsolute(dest_path));
97 const my_cwd = cwd();104 const my_cwd = cwd();
98 return Dir.updateFile(my_cwd, source_path, my_cwd, dest_path, .{ .override_mode = mode });105 return Dir.updateFile(my_cwd, source_path, my_cwd, dest_path, args);
99}
100
101/// Guaranteed to be atomic.
102/// On Linux, until https://patchwork.kernel.org/patch/9636735/ is merged and readily available,
103/// there is a possibility of power loss or application termination leaving temporary files present
104/// in the same directory as dest_path.
105/// Destination file will have the same mode as the source file.
106/// TODO rework this to integrate with Dir
107pub fn copyFile(source_path: []const u8, dest_path: []const u8) !void {
108 var in_file = try cwd().openFile(source_path, .{});
109 defer in_file.close();
110
111 const stat = try in_file.stat();
112
113 var atomic_file = try AtomicFile.init(dest_path, stat.mode);
114 defer atomic_file.deinit();
115
116 try atomic_file.file.writeFileAll(in_file, .{ .in_len = stat.size });
117 return atomic_file.finish();
118}106}
119107
120/// Guaranteed to be atomic.108/// Same as `Dir.copyFile`, except asserts that both `source_path` and `dest_path`
121/// On Linux, until https://patchwork.kernel.org/patch/9636735/ is merged and readily available,109/// are absolute. See `Dir.copyFile` for a function that operates on both
122/// there is a possibility of power loss or application termination leaving temporary files present110/// absolute and relative paths.
123/// in the same directory as dest_path.111pub fn copyFileAbsolute(source_path: []const u8, dest_path: []const u8, args: CopyFileOptions) !void {
124/// TODO rework this to integrate with Dir112 assert(path.isAbsolute(source_path));
125pub fn copyFileMode(source_path: []const u8, dest_path: []const u8, mode: File.Mode) !void {113 assert(path.isAbsolute(dest_path));
126 var in_file = try cwd().openFile(source_path, .{});114 const my_cwd = cwd();
127 defer in_file.close();115 return Dir.copyFile(my_cwd, source_path, my_cwd, dest_path, args);
128
129 var atomic_file = try AtomicFile.init(dest_path, mode);
130 defer atomic_file.deinit();
131
132 try atomic_file.file.writeFileAll(in_file, .{});
133 return atomic_file.finish();
134}116}
135117
136/// TODO update this API to avoid a getrandom syscall for every operation.118/// TODO update this API to avoid a getrandom syscall for every operation.
...@@ -245,18 +227,17 @@ pub fn makeDirAbsoluteW(absolute_path_w: [*:0]const u16) !void {...@@ -245,18 +227,17 @@ pub fn makeDirAbsoluteW(absolute_path_w: [*:0]const u16) !void {
245 os.windows.CloseHandle(handle);227 os.windows.CloseHandle(handle);
246}228}
247229
248/// Returns `error.DirNotEmpty` if the directory is not empty.230/// Deprecated; use `Dir.deleteDir`.
249/// To delete a directory recursively, see `deleteTree`.
250pub fn deleteDir(dir_path: []const u8) !void {231pub fn deleteDir(dir_path: []const u8) !void {
251 return os.rmdir(dir_path);232 return os.rmdir(dir_path);
252}233}
253234
254/// Same as `deleteDir` except the parameter is a null-terminated UTF8-encoded string.235/// Deprecated; use `Dir.deleteDirC`.
255pub fn deleteDirC(dir_path: [*:0]const u8) !void {236pub fn deleteDirC(dir_path: [*:0]const u8) !void {
256 return os.rmdirC(dir_path);237 return os.rmdirC(dir_path);
257}238}
258239
259/// Same as `deleteDir` except the parameter is a null-terminated UTF16LE-encoded string.240/// Deprecated; use `Dir.deleteDirW`.
260pub fn deleteDirW(dir_path: [*:0]const u16) !void {241pub fn deleteDirW(dir_path: [*:0]const u16) !void {
261 return os.rmdirW(dir_path);242 return os.rmdirW(dir_path);
262}243}
...@@ -1218,22 +1199,17 @@ pub const Dir = struct {...@@ -1218,22 +1199,17 @@ pub const Dir = struct {
1218 return os.faccessatW(self.fd, sub_path_w, 0, 0);1199 return os.faccessatW(self.fd, sub_path_w, 0, 0);
1219 }1200 }
12201201
1221 pub const UpdateFileOptions = struct {
1222 override_mode: ?File.Mode = null,
1223 };
1224
1225 /// Check the file size, mtime, and mode of `source_path` and `dest_path`. If they are equal, does nothing.1202 /// Check the file size, mtime, and mode of `source_path` and `dest_path`. If they are equal, does nothing.
1226 /// Otherwise, atomically copies `source_path` to `dest_path`. The destination file gains the mtime,1203 /// Otherwise, atomically copies `source_path` to `dest_path`. The destination file gains the mtime,
1227 /// atime, and mode of the source file so that the next call to `updateFile` will not need a copy.1204 /// atime, and mode of the source file so that the next call to `updateFile` will not need a copy.
1228 /// Returns the previous status of the file before updating.1205 /// Returns the previous status of the file before updating.
1229 /// If any of the directories do not exist for dest_path, they are created.1206 /// If any of the directories do not exist for dest_path, they are created.
1230 /// If `override_mode` is provided, then that value is used rather than the source path's mode.
1231 pub fn updateFile(1207 pub fn updateFile(
1232 source_dir: Dir,1208 source_dir: Dir,
1233 source_path: []const u8,1209 source_path: []const u8,
1234 dest_dir: Dir,1210 dest_dir: Dir,
1235 dest_path: []const u8,1211 dest_path: []const u8,
1236 options: UpdateFileOptions,1212 options: CopyFileOptions,
1237 ) !PrevStatus {1213 ) !PrevStatus {
1238 var src_file = try source_dir.openFile(source_path, .{});1214 var src_file = try source_dir.openFile(source_path, .{});
1239 defer src_file.close();1215 defer src_file.close();
...@@ -1272,6 +1248,34 @@ pub const Dir = struct {...@@ -1272,6 +1248,34 @@ pub const Dir = struct {
1272 return PrevStatus.stale;1248 return PrevStatus.stale;
1273 }1249 }
12741250
1251 /// Guaranteed to be atomic.
1252 /// On Linux, until https://patchwork.kernel.org/patch/9636735/ is merged and readily available,
1253 /// there is a possibility of power loss or application termination leaving temporary files present
1254 /// in the same directory as dest_path.
1255 pub fn copyFile(
1256 source_dir: Dir,
1257 source_path: []const u8,
1258 dest_dir: Dir,
1259 dest_path: []const u8,
1260 options: CopyFileOptions,
1261 ) !void {
1262 var in_file = try source_dir.openFile(source_path, .{});
1263 defer in_file.close();
1264
1265 var size: ?u64 = null;
1266 const mode = options.override_mode orelse blk: {
1267 const stat = try in_file.stat();
1268 size = stat.size;
1269 break :blk stat.mode;
1270 };
1271
1272 var atomic_file = try dest_dir.atomicFile(dest_path, .{ .mode = mode });
1273 defer atomic_file.deinit();
1274
1275 try atomic_file.file.writeFileAll(in_file, .{ .in_len = size });
1276 return atomic_file.finish();
1277 }
1278
1275 pub const AtomicFileOptions = struct {1279 pub const AtomicFileOptions = struct {
1276 mode: File.Mode = File.default_mode,1280 mode: File.Mode = File.default_mode,
1277 };1281 };
...@@ -1471,13 +1475,12 @@ pub fn walkPath(allocator: *Allocator, dir_path: []const u8) !Walker {...@@ -1471,13 +1475,12 @@ pub fn walkPath(allocator: *Allocator, dir_path: []const u8) !Walker {
1471 return walker;1475 return walker;
1472}1476}
14731477
1474/// Read value of a symbolic link.1478/// Deprecated; use `Dir.readLink`.
1475/// The return value is a slice of buffer, from index `0`.
1476pub fn readLink(pathname: []const u8, buffer: *[MAX_PATH_BYTES]u8) ![]u8 {1479pub fn readLink(pathname: []const u8, buffer: *[MAX_PATH_BYTES]u8) ![]u8 {
1477 return os.readlink(pathname, buffer);1480 return os.readlink(pathname, buffer);
1478}1481}
14791482
1480/// Same as `readLink`, except the parameter is null-terminated.1483/// Deprecated; use `Dir.readLinkC`.
1481pub fn readLinkC(pathname_c: [*]const u8, buffer: *[MAX_PATH_BYTES]u8) ![]u8 {1484pub fn readLinkC(pathname_c: [*]const u8, buffer: *[MAX_PATH_BYTES]u8) ![]u8 {
1482 return os.readlinkC(pathname_c, buffer);1485 return os.readlinkC(pathname_c, buffer);
1483}1486}
...@@ -1584,6 +1587,7 @@ pub fn selfExeDirPath(out_buffer: *[MAX_PATH_BYTES]u8) SelfExePathError![]const...@@ -1584,6 +1587,7 @@ pub fn selfExeDirPath(out_buffer: *[MAX_PATH_BYTES]u8) SelfExePathError![]const
1584}1587}
15851588
1586/// `realpath`, except caller must free the returned memory.1589/// `realpath`, except caller must free the returned memory.
1590/// TODO integrate with `Dir`
1587pub fn realpathAlloc(allocator: *Allocator, pathname: []const u8) ![]u8 {1591pub fn realpathAlloc(allocator: *Allocator, pathname: []const u8) ![]u8 {
1588 var buf: [MAX_PATH_BYTES]u8 = undefined;1592 var buf: [MAX_PATH_BYTES]u8 = undefined;
1589 return mem.dupe(allocator, u8, try os.realpath(pathname, &buf));1593 return mem.dupe(allocator, u8, try os.realpath(pathname, &buf));
...@@ -1592,6 +1596,9 @@ pub fn realpathAlloc(allocator: *Allocator, pathname: []const u8) ![]u8 {...@@ -1592,6 +1596,9 @@ pub fn realpathAlloc(allocator: *Allocator, pathname: []const u8) ![]u8 {
1592test "" {1596test "" {
1593 _ = makeDirAbsolute;1597 _ = makeDirAbsolute;
1594 _ = makeDirAbsoluteZ;1598 _ = makeDirAbsoluteZ;
1599 _ = copyFileAbsolute;
1600 _ = updateFileAbsolute;
1601 _ = Dir.copyFile;
1595 _ = @import("fs/path.zig");1602 _ = @import("fs/path.zig");
1596 _ = @import("fs/file.zig");1603 _ = @import("fs/file.zig");
1597 _ = @import("fs/get_app_data_dir.zig");1604 _ = @import("fs/get_app_data_dir.zig");
lib/std/os/test.zig+8-6
...@@ -113,14 +113,16 @@ test "fs.copyFile" {...@@ -113,14 +113,16 @@ test "fs.copyFile" {
113 const dest_file = "tmp_test_copy_file2.txt";113 const dest_file = "tmp_test_copy_file2.txt";
114 const dest_file2 = "tmp_test_copy_file3.txt";114 const dest_file2 = "tmp_test_copy_file3.txt";
115115
116 try fs.cwd().writeFile(src_file, data);116 const cwd = fs.cwd();
117 defer fs.cwd().deleteFile(src_file) catch {};
118117
119 try fs.copyFile(src_file, dest_file);118 try cwd.writeFile(src_file, data);
120 defer fs.cwd().deleteFile(dest_file) catch {};119 defer cwd.deleteFile(src_file) catch {};
121120
122 try fs.copyFileMode(src_file, dest_file2, File.default_mode);121 try cwd.copyFile(src_file, cwd, dest_file, .{});
123 defer fs.cwd().deleteFile(dest_file2) catch {};122 defer cwd.deleteFile(dest_file) catch {};
123
124 try cwd.copyFile(src_file, cwd, dest_file2, .{ .override_mode = File.default_mode });
125 defer cwd.deleteFile(dest_file2) catch {};
124126
125 try expectFileContents(dest_file, data);127 try expectFileContents(dest_file, data);
126 try expectFileContents(dest_file2, data);128 try expectFileContents(dest_file2, data);