1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
|
const std = @import("std");
const c = @cImport({
@cInclude("time.h");
});
pub const FileSwapper = struct {
allocator: std.mem.Allocator,
target_path: []const u8,
tmp_path: []const u8,
tmp: std.fs.File,
pub fn createAt(alloc: std.mem.Allocator, path: []const u8) !FileSwapper {
const target_dir = std.fs.path.dirname(path) orelse ".";
const target_name = std.fs.path.basename(path);
const tmp_name = try std.fmt.allocPrint(
alloc,
".{s}",
.{target_name},
);
defer alloc.free(tmp_name);
const tmp_path = try std.fs.path.join(
alloc,
&.{ target_dir, tmp_name },
);
const f = try std.fs.cwd().createFile(tmp_path, .{});
return .{
.allocator = alloc,
.target_path = path,
.tmp_path = tmp_path,
.tmp = f,
};
}
pub fn writer(self: FileSwapper) std.fs.File.Writer {
return self.tmp.writer();
}
pub fn close(self: FileSwapper) void {
defer self.allocator.free(self.tmp_path);
defer self.tmp.close();
// flush writes
self.tmp.sync() catch
@panic("sync tmp file");
// check if target path exists
var found = true;
std.fs.cwd().access(self.target_path, .{}) catch |e| switch (e) {
error.FileNotFound => found = false,
else => @panic("unable to stat target path"),
};
// swap file into final location
std.fs.rename(
std.fs.cwd(),
self.tmp_path,
std.fs.cwd(),
self.target_path,
) catch
@panic("rename tmp file");
}
};
pub fn replace_extension(alloc: std.mem.Allocator, p: []const u8, new: []const u8) ![]const u8 {
const dirname = std.fs.path.dirname(p) orelse ".";
const basename = try strip_extension(std.fs.path.basename(p));
return try join_path_and_filename(
alloc,
&.{dirname},
&.{ basename, ".", new },
);
}
// also trims off base paths
pub fn strip_extension(path: []const u8) ![]const u8 {
const ext = std.fs.path.extension(path);
const ext_idx = std.mem.indexOfPosLinear(u8, path, 0, ext);
std.debug.assert(ext_idx != null);
return path[0..ext_idx.?];
}
pub fn join_path_and_filename(alloc: std.mem.Allocator, paths: []const []const u8, filename_parts: []const []const u8) ![]const u8 {
const filename = try std.mem.concat(alloc, u8, filename_parts);
defer alloc.free(filename);
var p = try std.ArrayList([]const u8).initCapacity(alloc, paths.len + 1);
defer p.deinit();
p.appendSliceAssumeCapacity(paths);
p.appendAssumeCapacity(filename);
const paths2 = try p.toOwnedSlice();
defer alloc.free(paths2);
std.debug.assert(paths.len + 1 == paths2.len);
return try std.fs.path.join(alloc, paths2);
}
pub fn get_mtime(alloc: std.mem.Allocator, path: []const u8) ![]const u8 {
const attr = try alloc.create(std.os.linux.Stat);
defer alloc.destroy(attr);
const pathZ = try alloc.dupeZ(u8, path);
defer alloc.free(pathZ);
// use stat to get mtime
_ = std.os.linux.stat(pathZ, attr);
// use libc strftime to format time string
const mtime = attr.mtime();
const mtime_str = try alloc.alloc(u8, 512);
const n = c.strftime(
mtime_str.ptr,
mtime_str.len,
"%F",
c.localtime(&mtime.sec),
);
return try alloc.realloc(mtime_str, n);
}
|