| ... | @@ -11,41 +11,208 @@ const posix = os.posix; | ... | @@ -11,41 +11,208 @@ const posix = os.posix; |
| 11 | const c = @import("../c/index.zig"); | 11 | const c = @import("../c/index.zig"); |
| 12 | const cstr = @import("../cstr.zig"); | 12 | const cstr = @import("../cstr.zig"); |
| 13 | | 13 | |
| 14 | pub const sep = switch (builtin.os) { | 14 | pub const sep_windows = '\\'; |
| 15 | Os.windows => '\\', | 15 | pub const sep_posix = '/'; |
| 16 | else => '/', | 16 | pub const sep = if (is_windows) sep_windows else sep_posix; |
| 17 | }; | 17 | |
| 18 | pub const delimiter = switch (builtin.os) { | 18 | pub const delimiter_windows = ';'; |
| 19 | Os.windows => ';', | 19 | pub const delimiter_posix = ':'; |
| 20 | else => ':', | 20 | pub const delimiter = if (is_windows) delimiter_windows else delimiter_posix; |
| 21 | }; | 21 | |
| | 22 | const is_windows = builtin.os == builtin.Os.windows; |
| 22 | | 23 | |
| 23 | /// Naively combines a series of paths with the native path seperator. | 24 | /// Naively combines a series of paths with the native path seperator. |
| 24 | /// Allocates memory for the result, which must be freed by the caller. | 25 | /// Allocates memory for the result, which must be freed by the caller. |
| 25 | pub fn join(allocator: &Allocator, paths: ...) -> %[]u8 { | 26 | pub fn join(allocator: &Allocator, paths: ...) -> %[]u8 { |
| 26 | mem.join(allocator, sep, paths) | 27 | if (is_windows) { |
| | 28 | return joinWindows(allocator, paths); |
| | 29 | } else { |
| | 30 | return joinPosix(allocator, paths); |
| | 31 | } |
| | 32 | } |
| | 33 | |
| | 34 | pub fn joinWindows(allocator: &Allocator, paths: ...) -> %[]u8 { |
| | 35 | return mem.join(allocator, sep_windows, paths); |
| | 36 | } |
| | 37 | |
| | 38 | pub fn joinPosix(allocator: &Allocator, paths: ...) -> %[]u8 { |
| | 39 | return mem.join(allocator, sep_posix, paths); |
| 27 | } | 40 | } |
| 28 | | 41 | |
| 29 | test "os.path.join" { | 42 | test "os.path.join" { |
| 30 | assert(mem.eql(u8, %%join(&debug.global_allocator, "/a/b", "c"), "/a/b/c")); | 43 | assert(mem.eql(u8, %%joinWindows(&debug.global_allocator, "c:\\a\\b", "c"), "c:\\a\\b\\c")); |
| 31 | assert(mem.eql(u8, %%join(&debug.global_allocator, "/a/b/", "c"), "/a/b/c")); | 44 | assert(mem.eql(u8, %%joinWindows(&debug.global_allocator, "c:\\a\\b\\", "c"), "c:\\a\\b\\c")); |
| 32 | | 45 | |
| 33 | assert(mem.eql(u8, %%join(&debug.global_allocator, "/", "a", "b/", "c"), "/a/b/c")); | 46 | assert(mem.eql(u8, %%joinWindows(&debug.global_allocator, "c:\\", "a", "b\\", "c"), "c:\\a\\b\\c")); |
| 34 | assert(mem.eql(u8, %%join(&debug.global_allocator, "/a/", "b/", "c"), "/a/b/c")); | 47 | assert(mem.eql(u8, %%joinWindows(&debug.global_allocator, "c:\\a\\", "b\\", "c"), "c:\\a\\b\\c")); |
| 35 | | 48 | |
| 36 | assert(mem.eql(u8, %%join(&debug.global_allocator, "/home/andy/dev/zig/build/lib/zig/std", "io.zig"), | 49 | assert(mem.eql(u8, %%joinWindows(&debug.global_allocator, |
| | 50 | "c:\\home\\andy\\dev\\zig\\build\\lib\\zig\\std", "io.zig"), |
| | 51 | "c:\\home\\andy\\dev\\zig\\build\\lib\\zig\\std\\io.zig")); |
| | 52 | |
| | 53 | assert(mem.eql(u8, %%joinPosix(&debug.global_allocator, "/a/b", "c"), "/a/b/c")); |
| | 54 | assert(mem.eql(u8, %%joinPosix(&debug.global_allocator, "/a/b/", "c"), "/a/b/c")); |
| | 55 | |
| | 56 | assert(mem.eql(u8, %%joinPosix(&debug.global_allocator, "/", "a", "b/", "c"), "/a/b/c")); |
| | 57 | assert(mem.eql(u8, %%joinPosix(&debug.global_allocator, "/a/", "b/", "c"), "/a/b/c")); |
| | 58 | |
| | 59 | assert(mem.eql(u8, %%joinPosix(&debug.global_allocator, "/home/andy/dev/zig/build/lib/zig/std", "io.zig"), |
| 37 | "/home/andy/dev/zig/build/lib/zig/std/io.zig")); | 60 | "/home/andy/dev/zig/build/lib/zig/std/io.zig")); |
| 38 | } | 61 | } |
| 39 | | 62 | |
| 40 | pub fn isAbsolute(path: []const u8) -> bool { | 63 | pub fn isAbsolute(path: []const u8) -> bool { |
| 41 | switch (builtin.os) { | 64 | if (is_windows) { |
| 42 | Os.windows => @compileError("Unsupported OS"), | 65 | return isAbsoluteWindows(path); |
| 43 | else => return path[0] == sep, | 66 | } else { |
| | 67 | return isAbsolutePosix(path); |
| 44 | } | 68 | } |
| 45 | } | 69 | } |
| 46 | | 70 | |
| 47 | /// This function is like a series of `cd` statements executed one after another. | 71 | pub fn isAbsoluteWindows(path: []const u8) -> bool { |
| 48 | /// The result does not have a trailing path separator. | 72 | if (path[0] == '/') |
| | 73 | return true; |
| | 74 | |
| | 75 | if (path[0] == '\\') { |
| | 76 | return true; |
| | 77 | } |
| | 78 | if (path.len < 3) { |
| | 79 | return false; |
| | 80 | } |
| | 81 | if (path[1] == ':') { |
| | 82 | if (path[2] == '/') |
| | 83 | return true; |
| | 84 | if (path[2] == '\\') |
| | 85 | return true; |
| | 86 | } |
| | 87 | return false; |
| | 88 | } |
| | 89 | |
| | 90 | pub fn isAbsolutePosix(path: []const u8) -> bool { |
| | 91 | return path[0] == sep_posix; |
| | 92 | } |
| | 93 | |
| | 94 | test "os.path.isAbsoluteWindows" { |
| | 95 | testIsAbsoluteWindows("/", true); |
| | 96 | testIsAbsoluteWindows("//", true); |
| | 97 | testIsAbsoluteWindows("//server", true); |
| | 98 | testIsAbsoluteWindows("//server/file", true); |
| | 99 | testIsAbsoluteWindows("\\\\server\\file", true); |
| | 100 | testIsAbsoluteWindows("\\\\server", true); |
| | 101 | testIsAbsoluteWindows("\\\\", true); |
| | 102 | testIsAbsoluteWindows("c", false); |
| | 103 | testIsAbsoluteWindows("c:", false); |
| | 104 | testIsAbsoluteWindows("c:\\", true); |
| | 105 | testIsAbsoluteWindows("c:/", true); |
| | 106 | testIsAbsoluteWindows("c://", true); |
| | 107 | testIsAbsoluteWindows("C:/Users/", true); |
| | 108 | testIsAbsoluteWindows("C:\\Users\\", true); |
| | 109 | testIsAbsoluteWindows("C:cwd/another", false); |
| | 110 | testIsAbsoluteWindows("C:cwd\\another", false); |
| | 111 | testIsAbsoluteWindows("directory/directory", false); |
| | 112 | testIsAbsoluteWindows("directory\\directory", false); |
| | 113 | } |
| | 114 | |
| | 115 | test "os.path.isAbsolutePosix" { |
| | 116 | testIsAbsolutePosix("/home/foo", true); |
| | 117 | testIsAbsolutePosix("/home/foo/..", true); |
| | 118 | testIsAbsolutePosix("bar/", false); |
| | 119 | testIsAbsolutePosix("./baz", false); |
| | 120 | } |
| | 121 | |
| | 122 | fn testIsAbsoluteWindows(path: []const u8, expected_result: bool) { |
| | 123 | assert(isAbsoluteWindows(path) == expected_result); |
| | 124 | } |
| | 125 | |
| | 126 | fn testIsAbsolutePosix(path: []const u8, expected_result: bool) { |
| | 127 | assert(isAbsolutePosix(path) == expected_result); |
| | 128 | } |
| | 129 | |
| | 130 | pub fn drive(path: []const u8) -> ?[]const u8 { |
| | 131 | if (path.len < 2) |
| | 132 | return null; |
| | 133 | if (path[1] != ':') |
| | 134 | return null; |
| | 135 | return path[0..2]; |
| | 136 | } |
| | 137 | |
| | 138 | pub fn networkShare(path: []const u8) -> ?[]const u8 { |
| | 139 | if (path.len < "//a/b".len) |
| | 140 | return null; |
| | 141 | |
| | 142 | // TODO when I combined these together with `inline for` the compiler crashed |
| | 143 | { |
| | 144 | const this_sep = '/'; |
| | 145 | const two_sep = []u8{this_sep, this_sep}; |
| | 146 | if (mem.startsWith(u8, path, two_sep)) { |
| | 147 | if (path[2] == this_sep) |
| | 148 | return null; |
| | 149 | |
| | 150 | var it = mem.split(path, []u8{this_sep}); |
| | 151 | _ = (it.next() ?? return null); |
| | 152 | _ = (it.next() ?? return null); |
| | 153 | return path[0..it.index]; |
| | 154 | } |
| | 155 | } |
| | 156 | { |
| | 157 | const this_sep = '\\'; |
| | 158 | const two_sep = []u8{this_sep, this_sep}; |
| | 159 | if (mem.startsWith(u8, path, two_sep)) { |
| | 160 | if (path[2] == this_sep) |
| | 161 | return null; |
| | 162 | |
| | 163 | var it = mem.split(path, []u8{this_sep}); |
| | 164 | _ = (it.next() ?? return null); |
| | 165 | _ = (it.next() ?? return null); |
| | 166 | return path[0..it.index]; |
| | 167 | } |
| | 168 | } |
| | 169 | return null; |
| | 170 | } |
| | 171 | |
| | 172 | test "os.path.networkShare" { |
| | 173 | assert(mem.eql(u8, ??networkShare("//a/b"), "//a/b")); |
| | 174 | assert(mem.eql(u8, ??networkShare("\\\\a\\b"), "\\\\a\\b")); |
| | 175 | |
| | 176 | assert(networkShare("\\\\a\\") == null); |
| | 177 | } |
| | 178 | |
| | 179 | pub fn diskDesignator(path: []const u8) -> []const u8 { |
| | 180 | if (!is_windows) |
| | 181 | return ""; |
| | 182 | |
| | 183 | return drive(path) ?? (networkShare(path) ?? []u8{}); |
| | 184 | } |
| | 185 | |
| | 186 | // TODO ASCII is wrong, we actually need full unicode support to compare paths. |
| | 187 | fn networkShareServersEql(ns1: []const u8, ns2: []const u8) -> bool { |
| | 188 | const sep1 = ns1[0]; |
| | 189 | const sep2 = ns2[0]; |
| | 190 | |
| | 191 | var it1 = mem.split(ns1, []u8{sep1}); |
| | 192 | var it2 = mem.split(ns2, []u8{sep2}); |
| | 193 | |
| | 194 | return asciiEqlIgnoreCase(??it1.next(), ??it2.next()); |
| | 195 | } |
| | 196 | |
| | 197 | fn asciiUpper(byte: u8) -> u8 { |
| | 198 | return switch (byte) { |
| | 199 | 'a' ... 'z' => 'A' + (byte - 'a'), |
| | 200 | else => byte, |
| | 201 | }; |
| | 202 | } |
| | 203 | |
| | 204 | fn asciiEqlIgnoreCase(s1: []const u8, s2: []const u8) -> bool { |
| | 205 | if (s1.len != s2.len) |
| | 206 | return false; |
| | 207 | var i: usize = 0; |
| | 208 | while (i < s1.len) : (i += 1) { |
| | 209 | if (asciiUpper(s1[i]) != asciiUpper(s2[i])) |
| | 210 | return false; |
| | 211 | } |
| | 212 | return true; |
| | 213 | } |
| | 214 | |
| | 215 | /// Converts the command line arguments into a slice and calls `resolveSlice`. |
| 49 | pub fn resolve(allocator: &Allocator, args: ...) -> %[]u8 { | 216 | pub fn resolve(allocator: &Allocator, args: ...) -> %[]u8 { |
| 50 | var paths: [args.len][]const u8 = undefined; | 217 | var paths: [args.len][]const u8 = undefined; |
| 51 | comptime var arg_i = 0; | 218 | comptime var arg_i = 0; |
| ... | @@ -55,18 +222,178 @@ pub fn resolve(allocator: &Allocator, args: ...) -> %[]u8 { | ... | @@ -55,18 +222,178 @@ pub fn resolve(allocator: &Allocator, args: ...) -> %[]u8 { |
| 55 | return resolveSlice(allocator, paths); | 222 | return resolveSlice(allocator, paths); |
| 56 | } | 223 | } |
| 57 | | 224 | |
| | 225 | /// On Windows, this calls `resolveWindows` and on POSIX it calls `resolvePosix`. |
| 58 | pub fn resolveSlice(allocator: &Allocator, paths: []const []const u8) -> %[]u8 { | 226 | pub fn resolveSlice(allocator: &Allocator, paths: []const []const u8) -> %[]u8 { |
| 59 | if (builtin.os == builtin.Os.windows) { | 227 | if (is_windows) { |
| 60 | @compileError("TODO implement os.path.resolve for windows"); | 228 | return resolveWindows(allocator, paths); |
| | 229 | } else { |
| | 230 | return resolvePosix(allocator, paths); |
| 61 | } | 231 | } |
| 62 | if (paths.len == 0) | 232 | } |
| | 233 | |
| | 234 | /// This function is like a series of `cd` statements executed one after another. |
| | 235 | /// It resolves "." and "..". |
| | 236 | /// The result does not have a trailing path separator. |
| | 237 | /// If all paths are relative it uses the current working directory as a starting point. |
| | 238 | /// Each drive has its own current working directory. |
| | 239 | /// Path separators are canonicalized to '\\' and drives are canonicalized to capital letters. |
| | 240 | pub fn resolveWindows(allocator: &Allocator, paths: []const []const u8) -> %[]u8 { |
| | 241 | if (paths.len == 0) { |
| | 242 | assert(is_windows); // resolveWindows called on non windows can't use getCwd |
| 63 | return os.getCwd(allocator); | 243 | return os.getCwd(allocator); |
| | 244 | } |
| | 245 | |
| | 246 | // determine which drive we want to result with |
| | 247 | var result_drive_upcase: ?u8 = null; |
| | 248 | var have_abs = false; |
| | 249 | var first_index: usize = 0; |
| | 250 | var max_size: usize = 0; |
| | 251 | for (paths) |p, i| { |
| | 252 | const is_abs = isAbsoluteWindows(p); |
| | 253 | if (is_abs) { |
| | 254 | have_abs = true; |
| | 255 | first_index = i; |
| | 256 | max_size = 0; |
| | 257 | } |
| | 258 | if (drive(p)) |d| { |
| | 259 | result_drive_upcase = asciiUpper(d[0]); |
| | 260 | } else if (networkShare(p)) |_| { |
| | 261 | result_drive_upcase = null; |
| | 262 | } |
| | 263 | max_size += p.len + 1; |
| | 264 | } |
| | 265 | |
| | 266 | |
| | 267 | // if we will result with a drive, loop again to determine |
| | 268 | // which is the first time the drive is absolutely specified, if any |
| | 269 | // and count up the max bytes for paths related to this drive |
| | 270 | if (result_drive_upcase) |res_dr| { |
| | 271 | have_abs = false; |
| | 272 | first_index = 0; |
| | 273 | max_size = "_:".len; |
| | 274 | var correct_drive = false; |
| | 275 | |
| | 276 | for (paths) |p, i| { |
| | 277 | if (drive(p)) |dr| { |
| | 278 | correct_drive = asciiUpper(dr[0]) == res_dr; |
| | 279 | } else if (networkShare(p)) |_| { |
| | 280 | continue; |
| | 281 | } |
| | 282 | if (!correct_drive) { |
| | 283 | continue; |
| | 284 | } |
| | 285 | const is_abs = isAbsoluteWindows(p); |
| | 286 | if (is_abs) { |
| | 287 | first_index = i; |
| | 288 | max_size = "_:".len; |
| | 289 | have_abs = true; |
| | 290 | } |
| | 291 | max_size += p.len + 1; |
| | 292 | } |
| | 293 | } |
| | 294 | |
| | 295 | var drive_buf = "_:"; |
| | 296 | var result: []u8 = undefined; |
| | 297 | var result_index: usize = 0; |
| | 298 | var root_slice: []const u8 = undefined; |
| | 299 | |
| | 300 | if (have_abs) { |
| | 301 | result = %return allocator.alloc(u8, max_size); |
| | 302 | |
| | 303 | if (result_drive_upcase) |res_dr| { |
| | 304 | drive_buf[0] = res_dr; |
| | 305 | root_slice = drive_buf[0..]; |
| | 306 | |
| | 307 | mem.copy(u8, result, root_slice); |
| | 308 | result_index += root_slice.len; |
| | 309 | } else { |
| | 310 | // We know it looks like //a/b or \\a\b because of earlier code |
| | 311 | var it = mem.split(paths[first_index], "/\\"); |
| | 312 | const server_name = ??it.next(); |
| | 313 | const other_name = ??it.next(); |
| | 314 | |
| | 315 | result[result_index] = '\\'; |
| | 316 | result_index += 1; |
| | 317 | result[result_index] = '\\'; |
| | 318 | result_index += 1; |
| | 319 | mem.copy(u8, result[result_index..], server_name); |
| | 320 | result_index += server_name.len; |
| | 321 | result[result_index] = '\\'; |
| | 322 | result_index += 1; |
| | 323 | mem.copy(u8, result[result_index..], other_name); |
| | 324 | result_index += other_name.len; |
| | 325 | |
| | 326 | root_slice = result[0..result_index]; |
| | 327 | } |
| | 328 | } else { |
| | 329 | assert(is_windows); // resolveWindows called on non windows can't use getCwd |
| | 330 | // TODO get cwd for result_drive if applicable |
| | 331 | const cwd = %return os.getCwd(allocator); |
| | 332 | defer allocator.free(cwd); |
| | 333 | result = %return allocator.alloc(u8, max_size + cwd.len + 1); |
| | 334 | mem.copy(u8, result, cwd); |
| | 335 | result_index += cwd.len; |
| | 336 | |
| | 337 | root_slice = diskDesignator(result[0..result_index]); |
| | 338 | } |
| | 339 | %defer allocator.free(result); |
| | 340 | |
| | 341 | var correct_drive = true; |
| | 342 | for (paths[first_index..]) |p, i| { |
| | 343 | if (result_drive_upcase) |res_dr| { |
| | 344 | if (drive(p)) |dr| { |
| | 345 | correct_drive = asciiUpper(dr[0]) == res_dr; |
| | 346 | } else if (networkShare(p)) |_| { |
| | 347 | continue; |
| | 348 | } |
| | 349 | if (!correct_drive) { |
| | 350 | continue; |
| | 351 | } |
| | 352 | } |
| | 353 | var it = mem.split(p[diskDesignator(p).len..], "/\\"); |
| | 354 | while (it.next()) |component| { |
| | 355 | if (mem.eql(u8, component, ".")) { |
| | 356 | continue; |
| | 357 | } else if (mem.eql(u8, component, "..")) { |
| | 358 | while (true) { |
| | 359 | if (result_index == 0 or result_index == root_slice.len) |
| | 360 | break; |
| | 361 | result_index -= 1; |
| | 362 | if (result[result_index] == '\\' or result[result_index] == '/') |
| | 363 | break; |
| | 364 | } |
| | 365 | } else { |
| | 366 | result[result_index] = sep_windows; |
| | 367 | result_index += 1; |
| | 368 | mem.copy(u8, result[result_index..], component); |
| | 369 | result_index += component.len; |
| | 370 | } |
| | 371 | } |
| | 372 | } |
| | 373 | |
| | 374 | if (result_index == root_slice.len) { |
| | 375 | result[result_index] = '\\'; |
| | 376 | result_index += 1; |
| | 377 | } |
| | 378 | |
| | 379 | return result[0..result_index]; |
| | 380 | } |
| | 381 | |
| | 382 | /// This function is like a series of `cd` statements executed one after another. |
| | 383 | /// It resolves "." and "..". |
| | 384 | /// The result does not have a trailing path separator. |
| | 385 | /// If all paths are relative it uses the current working directory as a starting point. |
| | 386 | pub fn resolvePosix(allocator: &Allocator, paths: []const []const u8) -> %[]u8 { |
| | 387 | if (paths.len == 0) { |
| | 388 | assert(!is_windows); // resolvePosix called on windows can't use getCwd |
| | 389 | return os.getCwd(allocator); |
| | 390 | } |
| 64 | | 391 | |
| 65 | var first_index: usize = 0; | 392 | var first_index: usize = 0; |
| 66 | var have_abs = false; | 393 | var have_abs = false; |
| 67 | var max_size: usize = 0; | 394 | var max_size: usize = 0; |
| 68 | for (paths) |p, i| { | 395 | for (paths) |p, i| { |
| 69 | if (isAbsolute(p)) { | 396 | if (isAbsolutePosix(p)) { |
| 70 | first_index = i; | 397 | first_index = i; |
| 71 | have_abs = true; | 398 | have_abs = true; |
| 72 | max_size = 0; | 399 | max_size = 0; |
| ... | @@ -80,6 +407,7 @@ pub fn resolveSlice(allocator: &Allocator, paths: []const []const u8) -> %[]u8 { | ... | @@ -80,6 +407,7 @@ pub fn resolveSlice(allocator: &Allocator, paths: []const []const u8) -> %[]u8 { |
| 80 | if (have_abs) { | 407 | if (have_abs) { |
| 81 | result = %return allocator.alloc(u8, max_size); | 408 | result = %return allocator.alloc(u8, max_size); |
| 82 | } else { | 409 | } else { |
| | 410 | assert(!is_windows); // resolvePosix called on windows can't use getCwd |
| 83 | const cwd = %return os.getCwd(allocator); | 411 | const cwd = %return os.getCwd(allocator); |
| 84 | defer allocator.free(cwd); | 412 | defer allocator.free(cwd); |
| 85 | result = %return allocator.alloc(u8, max_size + cwd.len + 1); | 413 | result = %return allocator.alloc(u8, max_size + cwd.len + 1); |
| ... | @@ -89,7 +417,7 @@ pub fn resolveSlice(allocator: &Allocator, paths: []const []const u8) -> %[]u8 { | ... | @@ -89,7 +417,7 @@ pub fn resolveSlice(allocator: &Allocator, paths: []const []const u8) -> %[]u8 { |
| 89 | %defer allocator.free(result); | 417 | %defer allocator.free(result); |
| 90 | | 418 | |
| 91 | for (paths[first_index..]) |p, i| { | 419 | for (paths[first_index..]) |p, i| { |
| 92 | var it = mem.split(p, '/'); | 420 | var it = mem.split(p, "/"); |
| 93 | while (it.next()) |component| { | 421 | while (it.next()) |component| { |
| 94 | if (mem.eql(u8, component, ".")) { | 422 | if (mem.eql(u8, component, ".")) { |
| 95 | continue; | 423 | continue; |
| ... | @@ -119,22 +447,95 @@ pub fn resolveSlice(allocator: &Allocator, paths: []const []const u8) -> %[]u8 { | ... | @@ -119,22 +447,95 @@ pub fn resolveSlice(allocator: &Allocator, paths: []const []const u8) -> %[]u8 { |
| 119 | } | 447 | } |
| 120 | | 448 | |
| 121 | test "os.path.resolve" { | 449 | test "os.path.resolve" { |
| 122 | assert(mem.eql(u8, testResolve("/a/b", "c"), "/a/b/c")); | 450 | const cwd = %%os.getCwd(&debug.global_allocator); |
| 123 | assert(mem.eql(u8, testResolve("/a/b", "c", "//d", "e///"), "/d/e")); | 451 | if (is_windows) { |
| 124 | assert(mem.eql(u8, testResolve("/a/b/c", "..", "../"), "/a")); | 452 | assert(mem.eql(u8, testResolveWindows([][]const u8{"."}), cwd)); |
| 125 | assert(mem.eql(u8, testResolve("/", "..", ".."), "/")); | 453 | } else { |
| 126 | assert(mem.eql(u8, testResolve("/a/b/c/"), "/a/b/c")); | 454 | assert(mem.eql(u8, testResolvePosix([][]const u8{"a/b/c/", "../../.."}), cwd)); |
| | 455 | assert(mem.eql(u8, testResolvePosix([][]const u8{"."}), cwd)); |
| | 456 | } |
| | 457 | } |
| | 458 | |
| | 459 | test "os.path.resolveWindows" { |
| | 460 | assert(mem.eql(u8, testResolveWindows([][]const u8{"c:/blah\\blah", "d:/games", "c:../a"}), "C:\\blah\\a")); |
| | 461 | assert(mem.eql(u8, testResolveWindows([][]const u8{"c:/blah\\blah", "d:/games", "C:../a"}), "C:\\blah\\a")); |
| | 462 | assert(mem.eql(u8, testResolveWindows([][]const u8{"c:/ignore", "d:\\a/b\\c/d", "\\e.exe"}), "D:\\e.exe")); |
| | 463 | assert(mem.eql(u8, testResolveWindows([][]const u8{"c:/ignore", "c:/some/file"}), "C:\\some\\file")); |
| | 464 | assert(mem.eql(u8, testResolveWindows([][]const u8{"d:/ignore", "d:some/dir//"}), "D:\\ignore\\some\\dir")); |
| | 465 | assert(mem.eql(u8, testResolveWindows([][]const u8{"//server/share", "..", "relative\\"}), "\\\\server\\share\\relative")); |
| | 466 | assert(mem.eql(u8, testResolveWindows([][]const u8{"c:/", "//"}), "C:\\")); |
| | 467 | assert(mem.eql(u8, testResolveWindows([][]const u8{"c:/", "//dir"}), "C:\\dir")); |
| | 468 | assert(mem.eql(u8, testResolveWindows([][]const u8{"c:/", "//server/share"}), "\\\\server\\share\\")); |
| | 469 | assert(mem.eql(u8, testResolveWindows([][]const u8{"c:/", "//server//share"}), "\\\\server\\share\\")); |
| | 470 | assert(mem.eql(u8, testResolveWindows([][]const u8{"c:/", "///some//dir"}), "C:\\some\\dir")); |
| | 471 | assert(mem.eql(u8, testResolveWindows([][]const u8{"C:\\foo\\tmp.3\\", "..\\tmp.3\\cycles\\root.js"}), |
| | 472 | "C:\\foo\\tmp.3\\cycles\\root.js")); |
| | 473 | } |
| | 474 | |
| | 475 | test "os.path.resolvePosix" { |
| | 476 | assert(mem.eql(u8, testResolvePosix([][]const u8{"/a/b", "c"}), "/a/b/c")); |
| | 477 | assert(mem.eql(u8, testResolvePosix([][]const u8{"/a/b", "c", "//d", "e///"}), "/d/e")); |
| | 478 | assert(mem.eql(u8, testResolvePosix([][]const u8{"/a/b/c", "..", "../"}), "/a")); |
| | 479 | assert(mem.eql(u8, testResolvePosix([][]const u8{"/", "..", ".."}), "/")); |
| | 480 | assert(mem.eql(u8, testResolvePosix([][]const u8{"/a/b/c/"}), "/a/b/c")); |
| | 481 | |
| | 482 | assert(mem.eql(u8, testResolvePosix([][]const u8{"/var/lib", "../", "file/"}), "/var/file")); |
| | 483 | assert(mem.eql(u8, testResolvePosix([][]const u8{"/var/lib", "/../", "file/"}), "/file")); |
| | 484 | assert(mem.eql(u8, testResolvePosix([][]const u8{"/some/dir", ".", "/absolute/"}), "/absolute")); |
| | 485 | assert(mem.eql(u8, testResolvePosix([][]const u8{"/foo/tmp.3/", "../tmp.3/cycles/root.js"}), "/foo/tmp.3/cycles/root.js")); |
| | 486 | } |
| | 487 | |
| | 488 | fn testResolveWindows(paths: []const []const u8) -> []u8 { |
| | 489 | return %%resolveWindows(&debug.global_allocator, paths); |
| 127 | } | 490 | } |
| 128 | fn testResolve(args: ...) -> []u8 { | 491 | |
| 129 | return %%resolve(&debug.global_allocator, args); | 492 | fn testResolvePosix(paths: []const []const u8) -> []u8 { |
| | 493 | return %%resolvePosix(&debug.global_allocator, paths); |
| 130 | } | 494 | } |
| 131 | | 495 | |
| 132 | pub fn dirname(path: []const u8) -> []const u8 { | 496 | pub fn dirname(path: []const u8) -> []const u8 { |
| 133 | if (builtin.os == builtin.Os.windows) { | 497 | if (is_windows) { |
| 134 | @compileError("TODO implement os.path.dirname for windows"); | 498 | return dirnameWindows(path); |
| | 499 | } else { |
| | 500 | return dirnamePosix(path); |
| 135 | } | 501 | } |
| | 502 | } |
| | 503 | |
| | 504 | pub fn dirnameWindows(path: []const u8) -> []const u8 { |
| 136 | if (path.len == 0) | 505 | if (path.len == 0) |
| 137 | return path[0..0]; | 506 | return path[0..0]; |
| | 507 | |
| | 508 | const root_slice = diskDesignator(path); |
| | 509 | if (path.len == root_slice.len) |
| | 510 | return path; |
| | 511 | |
| | 512 | const have_root_slash = path.len > root_slice.len and (path[root_slice.len] == '/' or path[root_slice.len] == '\\'); |
| | 513 | |
| | 514 | var end_index: usize = path.len - 1; |
| | 515 | |
| | 516 | while ((path[end_index] == '/' or path[end_index] == '\\') and end_index > root_slice.len) { |
| | 517 | if (end_index == 0) |
| | 518 | return path[0..0]; |
| | 519 | end_index -= 1; |
| | 520 | } |
| | 521 | |
| | 522 | while (path[end_index] != '/' and path[end_index] != '\\' and end_index > root_slice.len) { |
| | 523 | if (end_index == 0) |
| | 524 | return path[0..0]; |
| | 525 | end_index -= 1; |
| | 526 | } |
| | 527 | |
| | 528 | if (have_root_slash and end_index == root_slice.len) { |
| | 529 | end_index += 1; |
| | 530 | } |
| | 531 | |
| | 532 | return path[0..end_index]; |
| | 533 | } |
| | 534 | |
| | 535 | pub fn dirnamePosix(path: []const u8) -> []const u8 { |
| | 536 | if (path.len == 0) |
| | 537 | return path[0..0]; |
| | 538 | |
| 138 | var end_index: usize = path.len - 1; | 539 | var end_index: usize = path.len - 1; |
| 139 | while (path[end_index] == '/') { | 540 | while (path[end_index] == '/') { |
| 140 | if (end_index == 0) | 541 | if (end_index == 0) |
| ... | @@ -154,25 +555,71 @@ pub fn dirname(path: []const u8) -> []const u8 { | ... | @@ -154,25 +555,71 @@ pub fn dirname(path: []const u8) -> []const u8 { |
| 154 | return path[0..end_index]; | 555 | return path[0..end_index]; |
| 155 | } | 556 | } |
| 156 | | 557 | |
| 157 | test "os.path.dirname" { | 558 | test "os.path.dirnamePosix" { |
| 158 | testDirname("/a/b/c", "/a/b"); | 559 | testDirnamePosix("/a/b/c", "/a/b"); |
| 159 | testDirname("/a/b/c///", "/a/b"); | 560 | testDirnamePosix("/a/b/c///", "/a/b"); |
| 160 | testDirname("/a", "/"); | 561 | testDirnamePosix("/a", "/"); |
| 161 | testDirname("/", "/"); | 562 | testDirnamePosix("/", "/"); |
| 162 | testDirname("////", "/"); | 563 | testDirnamePosix("////", "/"); |
| 163 | testDirname("", ""); | 564 | testDirnamePosix("", ""); |
| 164 | testDirname("a", ""); | 565 | testDirnamePosix("a", ""); |
| 165 | testDirname("a/", ""); | 566 | testDirnamePosix("a/", ""); |
| 166 | testDirname("a//", ""); | 567 | testDirnamePosix("a//", ""); |
| 167 | } | 568 | } |
| 168 | fn testDirname(input: []const u8, expected_output: []const u8) { | 569 | |
| 169 | assert(mem.eql(u8, dirname(input), expected_output)); | 570 | test "os.path.dirnameWindows" { |
| | 571 | testDirnameWindows("c:\\", "c:\\"); |
| | 572 | testDirnameWindows("c:\\foo", "c:\\"); |
| | 573 | testDirnameWindows("c:\\foo\\", "c:\\"); |
| | 574 | testDirnameWindows("c:\\foo\\bar", "c:\\foo"); |
| | 575 | testDirnameWindows("c:\\foo\\bar\\", "c:\\foo"); |
| | 576 | testDirnameWindows("c:\\foo\\bar\\baz", "c:\\foo\\bar"); |
| | 577 | testDirnameWindows("\\", "\\"); |
| | 578 | testDirnameWindows("\\foo", "\\"); |
| | 579 | testDirnameWindows("\\foo\\", "\\"); |
| | 580 | testDirnameWindows("\\foo\\bar", "\\foo"); |
| | 581 | testDirnameWindows("\\foo\\bar\\", "\\foo"); |
| | 582 | testDirnameWindows("\\foo\\bar\\baz", "\\foo\\bar"); |
| | 583 | testDirnameWindows("c:", "c:"); |
| | 584 | testDirnameWindows("c:foo", "c:"); |
| | 585 | testDirnameWindows("c:foo\\", "c:"); |
| | 586 | testDirnameWindows("c:foo\\bar", "c:foo"); |
| | 587 | testDirnameWindows("c:foo\\bar\\", "c:foo"); |
| | 588 | testDirnameWindows("c:foo\\bar\\baz", "c:foo\\bar"); |
| | 589 | testDirnameWindows("file:stream", ""); |
| | 590 | testDirnameWindows("dir\\file:stream", "dir"); |
| | 591 | testDirnameWindows("\\\\unc\\share", "\\\\unc\\share"); |
| | 592 | testDirnameWindows("\\\\unc\\share\\foo", "\\\\unc\\share\\"); |
| | 593 | testDirnameWindows("\\\\unc\\share\\foo\\", "\\\\unc\\share\\"); |
| | 594 | testDirnameWindows("\\\\unc\\share\\foo\\bar", "\\\\unc\\share\\foo"); |
| | 595 | testDirnameWindows("\\\\unc\\share\\foo\\bar\\", "\\\\unc\\share\\foo"); |
| | 596 | testDirnameWindows("\\\\unc\\share\\foo\\bar\\baz", "\\\\unc\\share\\foo\\bar"); |
| | 597 | testDirnameWindows("/a/b/", "/a"); |
| | 598 | testDirnameWindows("/a/b", "/a"); |
| | 599 | testDirnameWindows("/a", "/"); |
| | 600 | testDirnameWindows("", ""); |
| | 601 | testDirnameWindows("/", "/"); |
| | 602 | testDirnameWindows("////", "/"); |
| | 603 | testDirnameWindows("foo", ""); |
| | 604 | } |
| | 605 | |
| | 606 | fn testDirnamePosix(input: []const u8, expected_output: []const u8) { |
| | 607 | assert(mem.eql(u8, dirnamePosix(input), expected_output)); |
| | 608 | } |
| | 609 | |
| | 610 | fn testDirnameWindows(input: []const u8, expected_output: []const u8) { |
| | 611 | assert(mem.eql(u8, dirnameWindows(input), expected_output)); |
| 170 | } | 612 | } |
| 171 | | 613 | |
| 172 | pub fn basename(path: []const u8) -> []const u8 { | 614 | pub fn basename(path: []const u8) -> []const u8 { |
| 173 | if (builtin.os == builtin.Os.windows) { | 615 | if (is_windows) { |
| 174 | @compileError("TODO implement os.path.basename for windows"); | 616 | return basenameWindows(path); |
| | 617 | } else { |
| | 618 | return basenamePosix(path); |
| 175 | } | 619 | } |
| | 620 | } |
| | 621 | |
| | 622 | pub fn basenamePosix(path: []const u8) -> []const u8 { |
| 176 | if (path.len == 0) | 623 | if (path.len == 0) |
| 177 | return []u8{}; | 624 | return []u8{}; |
| 178 | | 625 | |
| ... | @@ -193,6 +640,38 @@ pub fn basename(path: []const u8) -> []const u8 { | ... | @@ -193,6 +640,38 @@ pub fn basename(path: []const u8) -> []const u8 { |
| 193 | return path[start_index + 1..end_index]; | 640 | return path[start_index + 1..end_index]; |
| 194 | } | 641 | } |
| 195 | | 642 | |
| | 643 | pub fn basenameWindows(path: []const u8) -> []const u8 { |
| | 644 | if (path.len == 0) |
| | 645 | return []u8{}; |
| | 646 | |
| | 647 | var end_index: usize = path.len - 1; |
| | 648 | while (true) { |
| | 649 | const byte = path[end_index]; |
| | 650 | if (byte == '/' or byte == '\\') { |
| | 651 | if (end_index == 0) |
| | 652 | return []u8{}; |
| | 653 | end_index -= 1; |
| | 654 | continue; |
| | 655 | } |
| | 656 | if (byte == ':' and end_index == 1) { |
| | 657 | return []u8{}; |
| | 658 | } |
| | 659 | break; |
| | 660 | } |
| | 661 | |
| | 662 | var start_index: usize = end_index; |
| | 663 | end_index += 1; |
| | 664 | while (path[start_index] != '/' and path[start_index] != '\\' and |
| | 665 | !(path[start_index] == ':' and start_index == 1)) |
| | 666 | { |
| | 667 | if (start_index == 0) |
| | 668 | return path[0..end_index]; |
| | 669 | start_index -= 1; |
| | 670 | } |
| | 671 | |
| | 672 | return path[start_index + 1..end_index]; |
| | 673 | } |
| | 674 | |
| 196 | test "os.path.basename" { | 675 | test "os.path.basename" { |
| 197 | testBasename("", ""); | 676 | testBasename("", ""); |
| 198 | testBasename("/", ""); | 677 | testBasename("/", ""); |
| ... | @@ -206,26 +685,137 @@ test "os.path.basename" { | ... | @@ -206,26 +685,137 @@ test "os.path.basename" { |
| 206 | testBasename("/aaa/b", "b"); | 685 | testBasename("/aaa/b", "b"); |
| 207 | testBasename("/a/b", "b"); | 686 | testBasename("/a/b", "b"); |
| 208 | testBasename("//a", "a"); | 687 | testBasename("//a", "a"); |
| | 688 | |
| | 689 | testBasenamePosix("\\dir\\basename.ext", "\\dir\\basename.ext"); |
| | 690 | testBasenamePosix("\\basename.ext", "\\basename.ext"); |
| | 691 | testBasenamePosix("basename.ext", "basename.ext"); |
| | 692 | testBasenamePosix("basename.ext\\", "basename.ext\\"); |
| | 693 | testBasenamePosix("basename.ext\\\\", "basename.ext\\\\"); |
| | 694 | testBasenamePosix("foo", "foo"); |
| | 695 | |
| | 696 | testBasenameWindows("\\dir\\basename.ext", "basename.ext"); |
| | 697 | testBasenameWindows("\\basename.ext", "basename.ext"); |
| | 698 | testBasenameWindows("basename.ext", "basename.ext"); |
| | 699 | testBasenameWindows("basename.ext\\", "basename.ext"); |
| | 700 | testBasenameWindows("basename.ext\\\\", "basename.ext"); |
| | 701 | testBasenameWindows("foo", "foo"); |
| | 702 | testBasenameWindows("C:", ""); |
| | 703 | testBasenameWindows("C:.", "."); |
| | 704 | testBasenameWindows("C:\\", ""); |
| | 705 | testBasenameWindows("C:\\dir\\base.ext", "base.ext"); |
| | 706 | testBasenameWindows("C:\\basename.ext", "basename.ext"); |
| | 707 | testBasenameWindows("C:basename.ext", "basename.ext"); |
| | 708 | testBasenameWindows("C:basename.ext\\", "basename.ext"); |
| | 709 | testBasenameWindows("C:basename.ext\\\\", "basename.ext"); |
| | 710 | testBasenameWindows("C:foo", "foo"); |
| | 711 | testBasenameWindows("file:stream", "file:stream"); |
| 209 | } | 712 | } |
| | 713 | |
| 210 | fn testBasename(input: []const u8, expected_output: []const u8) { | 714 | fn testBasename(input: []const u8, expected_output: []const u8) { |
| 211 | assert(mem.eql(u8, basename(input), expected_output)); | 715 | assert(mem.eql(u8, basename(input), expected_output)); |
| 212 | } | 716 | } |
| 213 | | 717 | |
| 214 | /// Returns the relative path from ::from to ::to. If ::from and ::to each | 718 | fn testBasenamePosix(input: []const u8, expected_output: []const u8) { |
| 215 | /// resolve to the same path (after calling ::resolve on each), a zero-length | 719 | assert(mem.eql(u8, basenamePosix(input), expected_output)); |
| | 720 | } |
| | 721 | |
| | 722 | fn testBasenameWindows(input: []const u8, expected_output: []const u8) { |
| | 723 | assert(mem.eql(u8, basenameWindows(input), expected_output)); |
| | 724 | } |
| | 725 | |
| | 726 | /// Returns the relative path from `from` to `to`. If `from` and `to` each |
| | 727 | /// resolve to the same path (after calling `resolve` on each), a zero-length |
| 216 | /// string is returned. | 728 | /// string is returned. |
| | 729 | /// On Windows this canonicalizes the drive to a capital letter and paths to `\\`. |
| 217 | pub fn relative(allocator: &Allocator, from: []const u8, to: []const u8) -> %[]u8 { | 730 | pub fn relative(allocator: &Allocator, from: []const u8, to: []const u8) -> %[]u8 { |
| 218 | if (builtin.os == builtin.Os.windows) { | 731 | if (is_windows) { |
| 219 | @compileError("TODO implement os.path.relative for windows"); | 732 | return relativeWindows(allocator, from, to); |
| | 733 | } else { |
| | 734 | return relativePosix(allocator, from, to); |
| 220 | } | 735 | } |
| 221 | const resolved_from = %return resolve(allocator, from); | 736 | } |
| | 737 | |
| | 738 | pub fn relativeWindows(allocator: &Allocator, from: []const u8, to: []const u8) -> %[]u8 { |
| | 739 | const resolved_from = %return resolveWindows(allocator, [][]const u8{from}); |
| 222 | defer allocator.free(resolved_from); | 740 | defer allocator.free(resolved_from); |
| 223 | | 741 | |
| 224 | const resolved_to = %return resolve(allocator, to); | 742 | var clean_up_resolved_to = true; |
| | 743 | const resolved_to = %return resolveWindows(allocator, [][]const u8{to}); |
| | 744 | defer if (clean_up_resolved_to) allocator.free(resolved_to); |
| | 745 | |
| | 746 | const result_is_to = if (drive(resolved_to)) |to_drive| { |
| | 747 | if (drive(resolved_from)) |from_drive| { |
| | 748 | asciiUpper(from_drive[0]) != asciiUpper(to_drive[0]) |
| | 749 | } else { |
| | 750 | true |
| | 751 | } |
| | 752 | } else if (networkShare(resolved_to)) |to_ns| { |
| | 753 | if (networkShare(resolved_from)) |from_ns| { |
| | 754 | !networkShareServersEql(to_ns, from_ns) |
| | 755 | } else { |
| | 756 | true |
| | 757 | } |
| | 758 | } else { |
| | 759 | unreachable |
| | 760 | }; |
| | 761 | if (result_is_to) { |
| | 762 | clean_up_resolved_to = false; |
| | 763 | return resolved_to; |
| | 764 | } |
| | 765 | |
| | 766 | var from_it = mem.split(resolved_from, "/\\"); |
| | 767 | var to_it = mem.split(resolved_to, "/\\"); |
| | 768 | while (true) { |
| | 769 | const from_component = from_it.next() ?? return mem.dupe(allocator, u8, to_it.rest()); |
| | 770 | const to_rest = to_it.rest(); |
| | 771 | if (to_it.next()) |to_component| { |
| | 772 | // TODO ASCII is wrong, we actually need full unicode support to compare paths. |
| | 773 | if (asciiEqlIgnoreCase(from_component, to_component)) |
| | 774 | continue; |
| | 775 | } |
| | 776 | var up_count: usize = 1; |
| | 777 | while (from_it.next()) |_| { |
| | 778 | up_count += 1; |
| | 779 | } |
| | 780 | const up_index_end = up_count * "..\\".len; |
| | 781 | const result = %return allocator.alloc(u8, up_index_end + to_rest.len); |
| | 782 | %defer allocator.free(result); |
| | 783 | |
| | 784 | var result_index: usize = 0; |
| | 785 | while (result_index < up_index_end) { |
| | 786 | result[result_index] = '.'; |
| | 787 | result_index += 1; |
| | 788 | result[result_index] = '.'; |
| | 789 | result_index += 1; |
| | 790 | result[result_index] = '\\'; |
| | 791 | result_index += 1; |
| | 792 | } |
| | 793 | // shave off the trailing slash |
| | 794 | result_index -= 1; |
| | 795 | |
| | 796 | var rest_it = mem.split(to_rest, "/\\"); |
| | 797 | while (rest_it.next()) |to_component| { |
| | 798 | result[result_index] = '\\'; |
| | 799 | result_index += 1; |
| | 800 | mem.copy(u8, result[result_index..], to_component); |
| | 801 | result_index += to_component.len; |
| | 802 | } |
| | 803 | |
| | 804 | return result[0..result_index]; |
| | 805 | } |
| | 806 | |
| | 807 | return []u8{}; |
| | 808 | } |
| | 809 | |
| | 810 | pub fn relativePosix(allocator: &Allocator, from: []const u8, to: []const u8) -> %[]u8 { |
| | 811 | const resolved_from = %return resolvePosix(allocator, [][]const u8{from}); |
| | 812 | defer allocator.free(resolved_from); |
| | 813 | |
| | 814 | const resolved_to = %return resolvePosix(allocator, [][]const u8{to}); |
| 225 | defer allocator.free(resolved_to); | 815 | defer allocator.free(resolved_to); |
| 226 | | 816 | |
| 227 | var from_it = mem.split(resolved_from, '/'); | 817 | var from_it = mem.split(resolved_from, "/"); |
| 228 | var to_it = mem.split(resolved_to, '/'); | 818 | var to_it = mem.split(resolved_to, "/"); |
| 229 | while (true) { | 819 | while (true) { |
| 230 | const from_component = from_it.next() ?? return mem.dupe(allocator, u8, to_it.rest()); | 820 | const from_component = from_it.next() ?? return mem.dupe(allocator, u8, to_it.rest()); |
| 231 | const to_rest = to_it.rest(); | 821 | const to_rest = to_it.rest(); |
| ... | @@ -263,21 +853,52 @@ pub fn relative(allocator: &Allocator, from: []const u8, to: []const u8) -> %[]u | ... | @@ -263,21 +853,52 @@ pub fn relative(allocator: &Allocator, from: []const u8, to: []const u8) -> %[]u |
| 263 | } | 853 | } |
| 264 | | 854 | |
| 265 | test "os.path.relative" { | 855 | test "os.path.relative" { |
| 266 | testRelative("/var/lib", "/var", ".."); | 856 | testRelativeWindows("c:/blah\\blah", "d:/games", "D:\\games"); |
| 267 | testRelative("/var/lib", "/bin", "../../bin"); | 857 | testRelativeWindows("c:/aaaa/bbbb", "c:/aaaa", ".."); |
| 268 | testRelative("/var/lib", "/var/lib", ""); | 858 | testRelativeWindows("c:/aaaa/bbbb", "c:/cccc", "..\\..\\cccc"); |
| 269 | testRelative("/var/lib", "/var/apache", "../apache"); | 859 | testRelativeWindows("c:/aaaa/bbbb", "c:/aaaa/bbbb", ""); |
| 270 | testRelative("/var/", "/var/lib", "lib"); | 860 | testRelativeWindows("c:/aaaa/bbbb", "c:/aaaa/cccc", "..\\cccc"); |
| 271 | testRelative("/", "/var/lib", "var/lib"); | 861 | testRelativeWindows("c:/aaaa/", "c:/aaaa/cccc", "cccc"); |
| 272 | testRelative("/foo/test", "/foo/test/bar/package.json", "bar/package.json"); | 862 | testRelativeWindows("c:/", "c:\\aaaa\\bbbb", "aaaa\\bbbb"); |
| 273 | testRelative("/Users/a/web/b/test/mails", "/Users/a/web/b", "../.."); | 863 | testRelativeWindows("c:/aaaa/bbbb", "d:\\", "D:\\"); |
| 274 | testRelative("/foo/bar/baz-quux", "/foo/bar/baz", "../baz"); | 864 | testRelativeWindows("c:/AaAa/bbbb", "c:/aaaa/bbbb", ""); |
| 275 | testRelative("/foo/bar/baz", "/foo/bar/baz-quux", "../baz-quux"); | 865 | testRelativeWindows("c:/aaaaa/", "c:/aaaa/cccc", "..\\aaaa\\cccc"); |
| 276 | testRelative("/baz-quux", "/baz", "../baz"); | 866 | testRelativeWindows("C:\\foo\\bar\\baz\\quux", "C:\\", "..\\..\\..\\.."); |
| 277 | testRelative("/baz", "/baz-quux", "../baz-quux"); | 867 | testRelativeWindows("C:\\foo\\test", "C:\\foo\\test\\bar\\package.json", "bar\\package.json"); |
| 278 | } | 868 | testRelativeWindows("C:\\foo\\bar\\baz-quux", "C:\\foo\\bar\\baz", "..\\baz"); |
| 279 | fn testRelative(from: []const u8, to: []const u8, expected_output: []const u8) { | 869 | testRelativeWindows("C:\\foo\\bar\\baz", "C:\\foo\\bar\\baz-quux", "..\\baz-quux"); |
| 280 | const result = %%relative(&debug.global_allocator, from, to); | 870 | testRelativeWindows("\\\\foo\\bar", "\\\\foo\\bar\\baz", "baz"); |
| | 871 | testRelativeWindows("\\\\foo\\bar\\baz", "\\\\foo\\bar", ".."); |
| | 872 | testRelativeWindows("\\\\foo\\bar\\baz-quux", "\\\\foo\\bar\\baz", "..\\baz"); |
| | 873 | testRelativeWindows("\\\\foo\\bar\\baz", "\\\\foo\\bar\\baz-quux", "..\\baz-quux"); |
| | 874 | testRelativeWindows("C:\\baz-quux", "C:\\baz", "..\\baz"); |
| | 875 | testRelativeWindows("C:\\baz", "C:\\baz-quux", "..\\baz-quux"); |
| | 876 | testRelativeWindows("\\\\foo\\baz-quux", "\\\\foo\\baz", "..\\baz"); |
| | 877 | testRelativeWindows("\\\\foo\\baz", "\\\\foo\\baz-quux", "..\\baz-quux"); |
| | 878 | testRelativeWindows("C:\\baz", "\\\\foo\\bar\\baz", "\\\\foo\\bar\\baz"); |
| | 879 | testRelativeWindows("\\\\foo\\bar\\baz", "C:\\baz", "C:\\baz"); |
| | 880 | |
| | 881 | testRelativePosix("/var/lib", "/var", ".."); |
| | 882 | testRelativePosix("/var/lib", "/bin", "../../bin"); |
| | 883 | testRelativePosix("/var/lib", "/var/lib", ""); |
| | 884 | testRelativePosix("/var/lib", "/var/apache", "../apache"); |
| | 885 | testRelativePosix("/var/", "/var/lib", "lib"); |
| | 886 | testRelativePosix("/", "/var/lib", "var/lib"); |
| | 887 | testRelativePosix("/foo/test", "/foo/test/bar/package.json", "bar/package.json"); |
| | 888 | testRelativePosix("/Users/a/web/b/test/mails", "/Users/a/web/b", "../.."); |
| | 889 | testRelativePosix("/foo/bar/baz-quux", "/foo/bar/baz", "../baz"); |
| | 890 | testRelativePosix("/foo/bar/baz", "/foo/bar/baz-quux", "../baz-quux"); |
| | 891 | testRelativePosix("/baz-quux", "/baz", "../baz"); |
| | 892 | testRelativePosix("/baz", "/baz-quux", "../baz-quux"); |
| | 893 | } |
| | 894 | |
| | 895 | fn testRelativePosix(from: []const u8, to: []const u8, expected_output: []const u8) { |
| | 896 | const result = %%relativePosix(&debug.global_allocator, from, to); |
| | 897 | assert(mem.eql(u8, result, expected_output)); |
| | 898 | } |
| | 899 | |
| | 900 | fn testRelativeWindows(from: []const u8, to: []const u8, expected_output: []const u8) { |
| | 901 | const result = %%relativeWindows(&debug.global_allocator, from, to); |
| 281 | assert(mem.eql(u8, result, expected_output)); | 902 | assert(mem.eql(u8, result, expected_output)); |
| 282 | } | 903 | } |
| 283 | | 904 | |