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
|
// https://github.com/MasterQ32/ftz/blob/3183b582211f8e38c1c3363c56753026ca45c11f/src/main.zig#L431-L509
// Thanks, Felix! We should get this into std perhaps.
const std = @import("std");
/// Resolves a unix-like path and removes all "." and ".." from it. Will not escape the root and can be used to sanitize inputs.
pub fn resolvePath(buffer: []u8, src_path: []const u8) ?[]u8 {
var end: usize = 0;
buffer[0] = '.';
var iter = std.mem.tokenize(src_path, "/");
while (iter.next()) |segment| {
if (end >= buffer.len) break;
if (std.mem.eql(u8, segment, ".")) {
continue;
} else if (std.mem.eql(u8, segment, "..")) {
while (true) {
if (end == 0)
break;
if (buffer[end] == '/') {
break;
}
end -= 1;
}
} else {
if (end + segment.len + 1 > buffer.len)
return error.BufferTooSmall;
const start = end;
buffer[end] = '/';
end += segment.len + 1;
std.mem.copy(u8, buffer[start + 1 .. end], segment);
}
}
const result = if (end == 0)
buffer[0 .. end + 1]
else
buffer[0..end];
if (std.mem.eql(u8, result, src_path)) {
return null;
}
return result;
}
fn testResolve(expected: []const u8, input: []const u8) !void {
var buffer: [1024]u8 = undefined;
const actual = try resolvePath(&buffer, input);
std.testing.expectEqualStrings(expected, actual);
}
test "resolvePath" {
try testResolve("/", "");
try testResolve("/", "/");
try testResolve("/", "////////////");
try testResolve("/a", "a");
try testResolve("/a", "/a");
try testResolve("/a", "////////////a");
try testResolve("/a", "////////////a///");
try testResolve("/a/b/c/d", "/a/b/c/d");
try testResolve("/a/b/d", "/a/b/c/../d");
try testResolve("/", "..");
try testResolve("/", "/..");
try testResolve("/", "/../../../..");
try testResolve("/a/b/c", "a/b/c/");
try testResolve("/new/date.txt", "/new/../../new/date.txt");
}
test "resolvePath overflow" {
var buf: [1]u8 = undefined;
std.testing.expectEqualStrings("/", try resolvePath(&buf, "/"));
std.testing.expectError(error.BufferTooSmall, resolvePath(&buf, "a")); // will resolve to "/a"
}
|