| ... | @@ -11,37 +11,221 @@ const posix = os.posix; | ... | @@ -11,37 +11,221 @@ 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")); |
| | 45 | |
| | 46 | assert(mem.eql(u8, %%joinWindows(&debug.global_allocator, "c:\\", "a", "b\\", "c"), "c:\\a\\b\\c")); |
| | 47 | assert(mem.eql(u8, %%joinWindows(&debug.global_allocator, "c:\\a\\", "b\\", "c"), "c:\\a\\b\\c")); |
| 32 | | 48 | |
| 33 | assert(mem.eql(u8, %%join(&debug.global_allocator, "/", "a", "b/", "c"), "/a/b/c")); | 49 | assert(mem.eql(u8, %%joinWindows(&debug.global_allocator, |
| 34 | assert(mem.eql(u8, %%join(&debug.global_allocator, "/a/", "b/", "c"), "/a/b/c")); | 50 | "c:\\home\\andy\\dev\\zig\\build\\lib\\zig\\std", "io.zig"), |
| | 51 | "c:\\home\\andy\\dev\\zig\\build\\lib\\zig\\std\\io.zig")); |
| 35 | | 52 | |
| 36 | assert(mem.eql(u8, %%join(&debug.global_allocator, "/home/andy/dev/zig/build/lib/zig/std", "io.zig"), | 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); |
| | 68 | } |
| | 69 | } |
| | 70 | |
| | 71 | pub fn isAbsoluteWindows(path: []const u8) -> bool { |
| | 72 | if (path[0] == '/') |
| | 73 | return true; |
| | 74 | |
| | 75 | if (path[0] == '\\') { |
| | 76 | return true; |
| | 77 | } |
| | 78 | if (path.len < 3) { |
| | 79 | return false; |
| 44 | } | 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 | { |
| | 143 | const this_sep = '/'; |
| | 144 | const two_sep = []u8{this_sep, this_sep}; |
| | 145 | if (mem.startsWith(u8, path, two_sep)) { |
| | 146 | if (path[2] == this_sep) |
| | 147 | return null; |
| | 148 | const index_host = mem.indexOfScalarPos(u8, path, 3, this_sep) ?? return null; |
| | 149 | const next_start = index_host + 1; |
| | 150 | if (next_start >= path.len) |
| | 151 | return null; |
| | 152 | const index_root = mem.indexOfScalarPos(u8, path, next_start, this_sep) ?? path.len; |
| | 153 | return path[0..index_root]; |
| | 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 | const index_host = mem.indexOfScalarPos(u8, path, 3, this_sep) ?? return null; |
| | 163 | const next_start = index_host + 1; |
| | 164 | if (next_start >= path.len) |
| | 165 | return null; |
| | 166 | const index_root = mem.indexOfScalarPos(u8, path, next_start, this_sep) ?? path.len; |
| | 167 | return path[0..index_root]; |
| | 168 | } |
| | 169 | } |
| | 170 | return null; |
| | 171 | } |
| | 172 | |
| | 173 | test "os.path.networkShare" { |
| | 174 | assert(mem.eql(u8, ??networkShare("//a/b"), "//a/b")); |
| | 175 | assert(mem.eql(u8, ??networkShare("\\\\a\\b"), "\\\\a\\b")); |
| | 176 | |
| | 177 | assert(networkShare("\\\\a\\") == null); |
| | 178 | } |
| | 179 | |
| | 180 | pub fn preferredSepWindows(path: []const u8) -> u8 { |
| | 181 | for (path) |byte| { |
| | 182 | if (byte == '/' or byte == '\\') { |
| | 183 | return byte; |
| | 184 | } |
| | 185 | } |
| | 186 | return sep_windows; |
| | 187 | } |
| | 188 | |
| | 189 | pub fn preferredSep(path: []const u8) -> u8 { |
| | 190 | if (is_windows) { |
| | 191 | return preferredSepWindows(path); |
| | 192 | } else { |
| | 193 | return sep_posix; |
| | 194 | } |
| | 195 | } |
| | 196 | |
| | 197 | pub fn root(path: []const u8) -> []const u8 { |
| | 198 | if (is_windows) { |
| | 199 | return rootWindows(path); |
| | 200 | } else { |
| | 201 | return rootPosix(path); |
| | 202 | } |
| | 203 | } |
| | 204 | |
| | 205 | pub fn rootWindows(path: []const u8) -> []const u8 { |
| | 206 | return drive(path) ?? (networkShare(path) ?? []u8{}); |
| | 207 | } |
| | 208 | |
| | 209 | pub fn rootPosix(path: []const u8) -> ?[]const u8 { |
| | 210 | if (path.len == 0 or path[0] != '/') |
| | 211 | return []u8{}; |
| | 212 | |
| | 213 | return path[0..1]; |
| | 214 | } |
| | 215 | |
| | 216 | pub fn drivesEqual(drive1: []const u8, drive2: []const u8) -> bool { |
| | 217 | assert(drive1.len == 2); |
| | 218 | assert(drive2.len == 2); |
| | 219 | assert(drive1[1] == ':'); |
| | 220 | assert(drive2[1] == ':'); |
| | 221 | return asciiLower(drive1[0]) == asciiLower(drive2[0]); |
| | 222 | } |
| | 223 | |
| | 224 | fn asciiLower(byte: u8) -> u8 { |
| | 225 | return switch (byte) { |
| | 226 | 'A' ... 'Z' => 'a' + (byte - 'A'), |
| | 227 | else => byte, |
| | 228 | }; |
| 45 | } | 229 | } |
| 46 | | 230 | |
| 47 | /// This function is like a series of `cd` statements executed one after another. | 231 | /// This function is like a series of `cd` statements executed one after another. |
| ... | @@ -56,17 +240,130 @@ pub fn resolve(allocator: &Allocator, args: ...) -> %[]u8 { | ... | @@ -56,17 +240,130 @@ pub fn resolve(allocator: &Allocator, args: ...) -> %[]u8 { |
| 56 | } | 240 | } |
| 57 | | 241 | |
| 58 | pub fn resolveSlice(allocator: &Allocator, paths: []const []const u8) -> %[]u8 { | 242 | pub fn resolveSlice(allocator: &Allocator, paths: []const []const u8) -> %[]u8 { |
| 59 | if (builtin.os == builtin.Os.windows) { | 243 | if (is_windows) { |
| 60 | @compileError("TODO implement os.path.resolve for windows"); | 244 | return resolveWindows(allocator, paths); |
| | 245 | } else { |
| | 246 | return resolvePosix(allocator, paths); |
| 61 | } | 247 | } |
| 62 | if (paths.len == 0) | 248 | } |
| | 249 | |
| | 250 | pub fn resolveWindows(allocator: &Allocator, paths: []const []const u8) -> %[]u8 { |
| | 251 | if (paths.len == 0) { |
| | 252 | assert(is_windows); // resolveWindows called on non windows can't use getCwd |
| 63 | return os.getCwd(allocator); | 253 | return os.getCwd(allocator); |
| | 254 | } |
| | 255 | |
| | 256 | // determine which drive we want to result with |
| | 257 | var result_drive: ?[]const u8 = null; |
| | 258 | var have_abs = false; |
| | 259 | var first_index: usize = 0; |
| | 260 | var max_size: usize = 0; |
| | 261 | for (paths) |p, i| { |
| | 262 | const is_abs = isAbsoluteWindows(p); |
| | 263 | if (is_abs) { |
| | 264 | have_abs = true; |
| | 265 | first_index = i; |
| | 266 | max_size = 0; |
| | 267 | } |
| | 268 | if (drive(p)) |d| { |
| | 269 | result_drive = d; |
| | 270 | } else if (is_abs) { |
| | 271 | result_drive = null; |
| | 272 | } |
| | 273 | max_size += p.len + 1; |
| | 274 | } |
| | 275 | |
| | 276 | // if we will result with a drive, loop again to determine |
| | 277 | // which is the first time the drive is absolutely specified, if any |
| | 278 | // and count up the max bytes for paths related to this drive |
| | 279 | if (result_drive) |res_dr| { |
| | 280 | have_abs = false; |
| | 281 | first_index = 0; |
| | 282 | max_size = 0; |
| | 283 | var correct_drive = false; |
| | 284 | |
| | 285 | for (paths) |p, i| { |
| | 286 | if (drive(p)) |dr| { |
| | 287 | correct_drive = drivesEqual(dr, res_dr); |
| | 288 | } |
| | 289 | if (!correct_drive) { |
| | 290 | continue; |
| | 291 | } |
| | 292 | const is_abs = isAbsoluteWindows(p); |
| | 293 | if (is_abs) { |
| | 294 | first_index = i; |
| | 295 | max_size = 0; |
| | 296 | } |
| | 297 | max_size += p.len + 1; |
| | 298 | } |
| | 299 | } |
| | 300 | |
| | 301 | var result: []u8 = undefined; |
| | 302 | var result_index: usize = 0; |
| | 303 | |
| | 304 | if (have_abs) { |
| | 305 | result = %return allocator.alloc(u8, max_size); |
| | 306 | mem.copy(u8, result, paths[first_index]); |
| | 307 | result_index += paths[first_index].len; |
| | 308 | first_index += 1; |
| | 309 | } else { |
| | 310 | assert(is_windows); // resolveWindows called on non windows can't use getCwd |
| | 311 | // TODO get cwd for result_drive if applicable |
| | 312 | const cwd = %return os.getCwd(allocator); |
| | 313 | defer allocator.free(cwd); |
| | 314 | result = %return allocator.alloc(u8, max_size + cwd.len + 1); |
| | 315 | mem.copy(u8, result, cwd); |
| | 316 | result_index += cwd.len; |
| | 317 | } |
| | 318 | %defer allocator.free(result); |
| | 319 | |
| | 320 | var correct_drive = false; |
| | 321 | const rootSlice = rootWindows(result[0..result_index]); |
| | 322 | const preferred_path_sep = preferredSepWindows(result[0..result_index]); |
| | 323 | for (paths[first_index..]) |p, i| { |
| | 324 | if (result_drive) |res_dr| { |
| | 325 | if (drive(p)) |dr| { |
| | 326 | correct_drive = drivesEqual(dr, res_dr); |
| | 327 | } |
| | 328 | if (!correct_drive) { |
| | 329 | continue; |
| | 330 | } |
| | 331 | } |
| | 332 | var it = mem.split(p, "/\\"); |
| | 333 | while (it.next()) |component| { |
| | 334 | if (mem.eql(u8, component, ".")) { |
| | 335 | continue; |
| | 336 | } else if (mem.eql(u8, component, "..")) { |
| | 337 | while (true) { |
| | 338 | if (result_index == 0 or result_index == rootSlice.len) |
| | 339 | break; |
| | 340 | result_index -= 1; |
| | 341 | if (result[result_index] == '\\' or result[result_index] == '/') |
| | 342 | break; |
| | 343 | } |
| | 344 | } else { |
| | 345 | result[result_index] = preferred_path_sep; |
| | 346 | result_index += 1; |
| | 347 | mem.copy(u8, result[result_index..], component); |
| | 348 | result_index += component.len; |
| | 349 | } |
| | 350 | } |
| | 351 | } |
| | 352 | |
| | 353 | return result[0..result_index]; |
| | 354 | } |
| | 355 | |
| | 356 | pub fn resolvePosix(allocator: &Allocator, paths: []const []const u8) -> %[]u8 { |
| | 357 | if (paths.len == 0) { |
| | 358 | assert(!is_windows); // resolvePosix called on windows can't use getCwd |
| | 359 | return os.getCwd(allocator); |
| | 360 | } |
| 64 | | 361 | |
| 65 | var first_index: usize = 0; | 362 | var first_index: usize = 0; |
| 66 | var have_abs = false; | 363 | var have_abs = false; |
| 67 | var max_size: usize = 0; | 364 | var max_size: usize = 0; |
| 68 | for (paths) |p, i| { | 365 | for (paths) |p, i| { |
| 69 | if (isAbsolute(p)) { | 366 | if (isAbsolutePosix(p)) { |
| 70 | first_index = i; | 367 | first_index = i; |
| 71 | have_abs = true; | 368 | have_abs = true; |
| 72 | max_size = 0; | 369 | max_size = 0; |
| ... | @@ -80,6 +377,7 @@ pub fn resolveSlice(allocator: &Allocator, paths: []const []const u8) -> %[]u8 { | ... | @@ -80,6 +377,7 @@ pub fn resolveSlice(allocator: &Allocator, paths: []const []const u8) -> %[]u8 { |
| 80 | if (have_abs) { | 377 | if (have_abs) { |
| 81 | result = %return allocator.alloc(u8, max_size); | 378 | result = %return allocator.alloc(u8, max_size); |
| 82 | } else { | 379 | } else { |
| | 380 | assert(!is_windows); // resolvePosix called on windows can't use getCwd |
| 83 | const cwd = %return os.getCwd(allocator); | 381 | const cwd = %return os.getCwd(allocator); |
| 84 | defer allocator.free(cwd); | 382 | defer allocator.free(cwd); |
| 85 | result = %return allocator.alloc(u8, max_size + cwd.len + 1); | 383 | result = %return allocator.alloc(u8, max_size + cwd.len + 1); |
| ... | @@ -89,7 +387,7 @@ pub fn resolveSlice(allocator: &Allocator, paths: []const []const u8) -> %[]u8 { | ... | @@ -89,7 +387,7 @@ pub fn resolveSlice(allocator: &Allocator, paths: []const []const u8) -> %[]u8 { |
| 89 | %defer allocator.free(result); | 387 | %defer allocator.free(result); |
| 90 | | 388 | |
| 91 | for (paths[first_index..]) |p, i| { | 389 | for (paths[first_index..]) |p, i| { |
| 92 | var it = mem.split(p, '/'); | 390 | var it = mem.split(p, "/"); |
| 93 | while (it.next()) |component| { | 391 | while (it.next()) |component| { |
| 94 | if (mem.eql(u8, component, ".")) { | 392 | if (mem.eql(u8, component, ".")) { |
| 95 | continue; | 393 | continue; |
| ... | @@ -119,22 +417,95 @@ pub fn resolveSlice(allocator: &Allocator, paths: []const []const u8) -> %[]u8 { | ... | @@ -119,22 +417,95 @@ pub fn resolveSlice(allocator: &Allocator, paths: []const []const u8) -> %[]u8 { |
| 119 | } | 417 | } |
| 120 | | 418 | |
| 121 | test "os.path.resolve" { | 419 | test "os.path.resolve" { |
| 122 | assert(mem.eql(u8, testResolve("/a/b", "c"), "/a/b/c")); | 420 | const cwd = %%os.getCwd(&debug.global_allocator); |
| 123 | assert(mem.eql(u8, testResolve("/a/b", "c", "//d", "e///"), "/d/e")); | 421 | if (is_windows) { |
| 124 | assert(mem.eql(u8, testResolve("/a/b/c", "..", "../"), "/a")); | 422 | assert(mem.eql(u8, testResolveWindows([][]const u8{"."}), cwd)); |
| 125 | assert(mem.eql(u8, testResolve("/", "..", ".."), "/")); | 423 | } else { |
| 126 | assert(mem.eql(u8, testResolve("/a/b/c/"), "/a/b/c")); | 424 | assert(mem.eql(u8, testResolvePosix([][]const u8{"a/b/c/", "../../.."}), cwd)); |
| | 425 | assert(mem.eql(u8, testResolvePosix([][]const u8{"."}), cwd)); |
| | 426 | } |
| | 427 | } |
| | 428 | |
| | 429 | test "os.path.resolveWindows" { |
| | 430 | assert(mem.eql(u8, testResolveWindows([][]const u8{"c:/blah\\blah", "d:/games", "c:../a"}), "c:\\blah\\a")); |
| | 431 | assert(mem.eql(u8, testResolveWindows([][]const u8{"c:/blah\\blah", "d:/games", "C:../a"}), "c:\\blah\\a")); |
| | 432 | assert(mem.eql(u8, testResolveWindows([][]const u8{"c:/ignore", "d:\\a/b\\c/d", "\\e.exe"}), "d:\\e.exe")); |
| | 433 | assert(mem.eql(u8, testResolveWindows([][]const u8{"c:/ignore", "c:/some/file"}), "c:\\some\\file")); |
| | 434 | assert(mem.eql(u8, testResolveWindows([][]const u8{"d:/ignore", "d:some/dir//"}), "d:\\ignore\\some\\dir")); |
| | 435 | assert(mem.eql(u8, testResolveWindows([][]const u8{"//server/share", "..", "relative\\"}), "\\\\server\\share\\relative")); |
| | 436 | assert(mem.eql(u8, testResolveWindows([][]const u8{"c:/", "//"}), "c:\\")); |
| | 437 | assert(mem.eql(u8, testResolveWindows([][]const u8{"c:/", "//dir"}), "c:\\dir")); |
| | 438 | assert(mem.eql(u8, testResolveWindows([][]const u8{"c:/", "//server/share"}), "\\\\server\\share\\")); |
| | 439 | assert(mem.eql(u8, testResolveWindows([][]const u8{"c:/", "//server//share"}), "\\\\server\\share\\")); |
| | 440 | assert(mem.eql(u8, testResolveWindows([][]const u8{"c:/", "///some//dir"}), "c:\\some\\dir")); |
| | 441 | assert(mem.eql(u8, testResolveWindows([][]const u8{"C:\\foo\\tmp.3\\", "..\\tmp.3\\cycles\\root.js"}), |
| | 442 | "C:\\foo\\tmp.3\\cycles\\root.js")); |
| | 443 | } |
| | 444 | |
| | 445 | test "os.path.resolvePosix" { |
| | 446 | assert(mem.eql(u8, testResolvePosix([][]const u8{"/a/b", "c"}), "/a/b/c")); |
| | 447 | assert(mem.eql(u8, testResolvePosix([][]const u8{"/a/b", "c", "//d", "e///"}), "/d/e")); |
| | 448 | assert(mem.eql(u8, testResolvePosix([][]const u8{"/a/b/c", "..", "../"}), "/a")); |
| | 449 | assert(mem.eql(u8, testResolvePosix([][]const u8{"/", "..", ".."}), "/")); |
| | 450 | assert(mem.eql(u8, testResolvePosix([][]const u8{"/a/b/c/"}), "/a/b/c")); |
| | 451 | |
| | 452 | assert(mem.eql(u8, testResolvePosix([][]const u8{"/var/lib", "../", "file/"}), "/var/file")); |
| | 453 | assert(mem.eql(u8, testResolvePosix([][]const u8{"/var/lib", "/../", "file/"}), "/file")); |
| | 454 | assert(mem.eql(u8, testResolvePosix([][]const u8{"/some/dir", ".", "/absolute/"}), "/absolute")); |
| | 455 | assert(mem.eql(u8, testResolvePosix([][]const u8{"/foo/tmp.3/", "../tmp.3/cycles/root.js"}), "/foo/tmp.3/cycles/root.js")); |
| | 456 | } |
| | 457 | |
| | 458 | fn testResolveWindows(paths: []const []const u8) -> []u8 { |
| | 459 | return %%resolveWindows(&debug.global_allocator, paths); |
| 127 | } | 460 | } |
| 128 | fn testResolve(args: ...) -> []u8 { | 461 | |
| 129 | return %%resolve(&debug.global_allocator, args); | 462 | fn testResolvePosix(paths: []const []const u8) -> []u8 { |
| | 463 | return %%resolvePosix(&debug.global_allocator, paths); |
| 130 | } | 464 | } |
| 131 | | 465 | |
| 132 | pub fn dirname(path: []const u8) -> []const u8 { | 466 | pub fn dirname(path: []const u8) -> []const u8 { |
| 133 | if (builtin.os == builtin.Os.windows) { | 467 | if (is_windows) { |
| 134 | @compileError("TODO implement os.path.dirname for windows"); | 468 | return dirnameWindows(path); |
| | 469 | } else { |
| | 470 | return dirnamePosix(path); |
| | 471 | } |
| | 472 | } |
| | 473 | |
| | 474 | pub fn dirnameWindows(path: []const u8) -> []const u8 { |
| | 475 | if (path.len == 0) |
| | 476 | return path[0..0]; |
| | 477 | |
| | 478 | const rootSlice = rootWindows(path); |
| | 479 | if (path.len == rootSlice.len) |
| | 480 | return path; |
| | 481 | |
| | 482 | const have_root_slash = path.len > rootSlice.len and (path[rootSlice.len] == '/' or path[rootSlice.len] == '\\'); |
| | 483 | |
| | 484 | var end_index: usize = path.len - 1; |
| | 485 | |
| | 486 | while ((path[end_index] == '/' or path[end_index] == '\\') and end_index > rootSlice.len) { |
| | 487 | if (end_index == 0) |
| | 488 | return path[0..0]; |
| | 489 | end_index -= 1; |
| | 490 | } |
| | 491 | |
| | 492 | while (path[end_index] != '/' and path[end_index] != '\\' and end_index > rootSlice.len) { |
| | 493 | if (end_index == 0) |
| | 494 | return path[0..0]; |
| | 495 | end_index -= 1; |
| | 496 | } |
| | 497 | |
| | 498 | if (have_root_slash and end_index == rootSlice.len) { |
| | 499 | end_index += 1; |
| 135 | } | 500 | } |
| | 501 | |
| | 502 | return path[0..end_index]; |
| | 503 | } |
| | 504 | |
| | 505 | pub fn dirnamePosix(path: []const u8) -> []const u8 { |
| 136 | if (path.len == 0) | 506 | if (path.len == 0) |
| 137 | return path[0..0]; | 507 | return path[0..0]; |
| | 508 | |
| 138 | var end_index: usize = path.len - 1; | 509 | var end_index: usize = path.len - 1; |
| 139 | while (path[end_index] == '/') { | 510 | while (path[end_index] == '/') { |
| 140 | if (end_index == 0) | 511 | if (end_index == 0) |
| ... | @@ -154,19 +525,60 @@ pub fn dirname(path: []const u8) -> []const u8 { | ... | @@ -154,19 +525,60 @@ pub fn dirname(path: []const u8) -> []const u8 { |
| 154 | return path[0..end_index]; | 525 | return path[0..end_index]; |
| 155 | } | 526 | } |
| 156 | | 527 | |
| 157 | test "os.path.dirname" { | 528 | test "os.path.dirnamePosix" { |
| 158 | testDirname("/a/b/c", "/a/b"); | 529 | testDirnamePosix("/a/b/c", "/a/b"); |
| 159 | testDirname("/a/b/c///", "/a/b"); | 530 | testDirnamePosix("/a/b/c///", "/a/b"); |
| 160 | testDirname("/a", "/"); | 531 | testDirnamePosix("/a", "/"); |
| 161 | testDirname("/", "/"); | 532 | testDirnamePosix("/", "/"); |
| 162 | testDirname("////", "/"); | 533 | testDirnamePosix("////", "/"); |
| 163 | testDirname("", ""); | 534 | testDirnamePosix("", ""); |
| 164 | testDirname("a", ""); | 535 | testDirnamePosix("a", ""); |
| 165 | testDirname("a/", ""); | 536 | testDirnamePosix("a/", ""); |
| 166 | testDirname("a//", ""); | 537 | testDirnamePosix("a//", ""); |
| | 538 | } |
| | 539 | |
| | 540 | test "os.path.dirnameWindows" { |
| | 541 | testDirnameWindows("c:\\", "c:\\"); |
| | 542 | testDirnameWindows("c:\\foo", "c:\\"); |
| | 543 | testDirnameWindows("c:\\foo\\", "c:\\"); |
| | 544 | testDirnameWindows("c:\\foo\\bar", "c:\\foo"); |
| | 545 | testDirnameWindows("c:\\foo\\bar\\", "c:\\foo"); |
| | 546 | testDirnameWindows("c:\\foo\\bar\\baz", "c:\\foo\\bar"); |
| | 547 | testDirnameWindows("\\", "\\"); |
| | 548 | testDirnameWindows("\\foo", "\\"); |
| | 549 | testDirnameWindows("\\foo\\", "\\"); |
| | 550 | testDirnameWindows("\\foo\\bar", "\\foo"); |
| | 551 | testDirnameWindows("\\foo\\bar\\", "\\foo"); |
| | 552 | testDirnameWindows("\\foo\\bar\\baz", "\\foo\\bar"); |
| | 553 | testDirnameWindows("c:", "c:"); |
| | 554 | testDirnameWindows("c:foo", "c:"); |
| | 555 | testDirnameWindows("c:foo\\", "c:"); |
| | 556 | testDirnameWindows("c:foo\\bar", "c:foo"); |
| | 557 | testDirnameWindows("c:foo\\bar\\", "c:foo"); |
| | 558 | testDirnameWindows("c:foo\\bar\\baz", "c:foo\\bar"); |
| | 559 | testDirnameWindows("file:stream", ""); |
| | 560 | testDirnameWindows("dir\\file:stream", "dir"); |
| | 561 | testDirnameWindows("\\\\unc\\share", "\\\\unc\\share"); |
| | 562 | testDirnameWindows("\\\\unc\\share\\foo", "\\\\unc\\share\\"); |
| | 563 | testDirnameWindows("\\\\unc\\share\\foo\\", "\\\\unc\\share\\"); |
| | 564 | testDirnameWindows("\\\\unc\\share\\foo\\bar", "\\\\unc\\share\\foo"); |
| | 565 | testDirnameWindows("\\\\unc\\share\\foo\\bar\\", "\\\\unc\\share\\foo"); |
| | 566 | testDirnameWindows("\\\\unc\\share\\foo\\bar\\baz", "\\\\unc\\share\\foo\\bar"); |
| | 567 | testDirnameWindows("/a/b/", "/a"); |
| | 568 | testDirnameWindows("/a/b", "/a"); |
| | 569 | testDirnameWindows("/a", "/"); |
| | 570 | testDirnameWindows("", ""); |
| | 571 | testDirnameWindows("/", "/"); |
| | 572 | testDirnameWindows("////", "/"); |
| | 573 | testDirnameWindows("foo", ""); |
| 167 | } | 574 | } |
| 168 | fn testDirname(input: []const u8, expected_output: []const u8) { | 575 | |
| 169 | assert(mem.eql(u8, dirname(input), expected_output)); | 576 | fn testDirnamePosix(input: []const u8, expected_output: []const u8) { |
| | 577 | assert(mem.eql(u8, dirnamePosix(input), expected_output)); |
| | 578 | } |
| | 579 | |
| | 580 | fn testDirnameWindows(input: []const u8, expected_output: []const u8) { |
| | 581 | assert(mem.eql(u8, dirnameWindows(input), expected_output)); |
| 170 | } | 582 | } |
| 171 | | 583 | |
| 172 | pub fn basename(path: []const u8) -> []const u8 { | 584 | pub fn basename(path: []const u8) -> []const u8 { |
| ... | @@ -215,9 +627,18 @@ fn testBasename(input: []const u8, expected_output: []const u8) { | ... | @@ -215,9 +627,18 @@ fn testBasename(input: []const u8, expected_output: []const u8) { |
| 215 | /// resolve to the same path (after calling ::resolve on each), a zero-length | 627 | /// resolve to the same path (after calling ::resolve on each), a zero-length |
| 216 | /// string is returned. | 628 | /// string is returned. |
| 217 | pub fn relative(allocator: &Allocator, from: []const u8, to: []const u8) -> %[]u8 { | 629 | pub fn relative(allocator: &Allocator, from: []const u8, to: []const u8) -> %[]u8 { |
| 218 | if (builtin.os == builtin.Os.windows) { | 630 | if (is_windows) { |
| 219 | @compileError("TODO implement os.path.relative for windows"); | 631 | return windowsRelative(allocator, from, to); |
| | 632 | } else { |
| | 633 | return posixRelative(allocator, from, to); |
| 220 | } | 634 | } |
| | 635 | } |
| | 636 | |
| | 637 | fn windowsRelative(allocator: &Allocator, from: []const u8, to: []const u8) -> %[]u8 { |
| | 638 | @compileError("TODO implement this"); |
| | 639 | } |
| | 640 | |
| | 641 | fn posixRelative(allocator: &Allocator, from: []const u8, to: []const u8) -> %[]u8 { |
| 221 | const resolved_from = %return resolve(allocator, from); | 642 | const resolved_from = %return resolve(allocator, from); |
| 222 | defer allocator.free(resolved_from); | 643 | defer allocator.free(resolved_from); |
| 223 | | 644 | |
| ... | @@ -263,18 +684,45 @@ pub fn relative(allocator: &Allocator, from: []const u8, to: []const u8) -> %[]u | ... | @@ -263,18 +684,45 @@ pub fn relative(allocator: &Allocator, from: []const u8, to: []const u8) -> %[]u |
| 263 | } | 684 | } |
| 264 | | 685 | |
| 265 | test "os.path.relative" { | 686 | test "os.path.relative" { |
| 266 | testRelative("/var/lib", "/var", ".."); | 687 | if (is_windows) { |
| 267 | testRelative("/var/lib", "/bin", "../../bin"); | 688 | testRelative("c:/blah\\blah", "d:/games", "d:\\games"); |
| 268 | testRelative("/var/lib", "/var/lib", ""); | 689 | testRelative("c:/aaaa/bbbb", "c:/aaaa", ".."); |
| 269 | testRelative("/var/lib", "/var/apache", "../apache"); | 690 | testRelative("c:/aaaa/bbbb", "c:/cccc", "..\\..\\cccc"); |
| 270 | testRelative("/var/", "/var/lib", "lib"); | 691 | testRelative("c:/aaaa/bbbb", "c:/aaaa/bbbb", ""); |
| 271 | testRelative("/", "/var/lib", "var/lib"); | 692 | testRelative("c:/aaaa/bbbb", "c:/aaaa/cccc", "..\\cccc"); |
| 272 | testRelative("/foo/test", "/foo/test/bar/package.json", "bar/package.json"); | 693 | testRelative("c:/aaaa/", "c:/aaaa/cccc", "cccc"); |
| 273 | testRelative("/Users/a/web/b/test/mails", "/Users/a/web/b", "../.."); | 694 | testRelative("c:/", "c:\\aaaa\\bbbb", "aaaa\\bbbb"); |
| 274 | testRelative("/foo/bar/baz-quux", "/foo/bar/baz", "../baz"); | 695 | testRelative("c:/aaaa/bbbb", "d:\\", "d:\\"); |
| 275 | testRelative("/foo/bar/baz", "/foo/bar/baz-quux", "../baz-quux"); | 696 | testRelative("c:/AaAa/bbbb", "c:/aaaa/bbbb", ""); |
| 276 | testRelative("/baz-quux", "/baz", "../baz"); | 697 | testRelative("c:/aaaaa/", "c:/aaaa/cccc", "..\\aaaa\\cccc"); |
| 277 | testRelative("/baz", "/baz-quux", "../baz-quux"); | 698 | testRelative("C:\\foo\\bar\\baz\\quux", "C:\\", "..\\..\\..\\.."); |
| | 699 | testRelative("C:\\foo\\test", "C:\\foo\\test\\bar\\package.json", "bar\\package.json"); |
| | 700 | testRelative("C:\\foo\\bar\\baz-quux", "C:\\foo\\bar\\baz", "..\\baz"); |
| | 701 | testRelative("C:\\foo\\bar\\baz", "C:\\foo\\bar\\baz-quux", "..\\baz-quux"); |
| | 702 | testRelative("\\\\foo\\bar", "\\\\foo\\bar\\baz", "baz"); |
| | 703 | testRelative("\\\\foo\\bar\\baz", "\\\\foo\\bar", ".."); |
| | 704 | testRelative("\\\\foo\\bar\\baz-quux", "\\\\foo\\bar\\baz", "..\\baz"); |
| | 705 | testRelative("\\\\foo\\bar\\baz", "\\\\foo\\bar\\baz-quux", "..\\baz-quux"); |
| | 706 | testRelative("C:\\baz-quux", "C:\\baz", "..\\baz"); |
| | 707 | testRelative("C:\\baz", "C:\\baz-quux", "..\\baz-quux"); |
| | 708 | testRelative("\\\\foo\\baz-quux", "\\\\foo\\baz", "..\\baz"); |
| | 709 | testRelative("\\\\foo\\baz", "\\\\foo\\baz-quux", "..\\baz-quux"); |
| | 710 | testRelative("C:\\baz", "\\\\foo\\bar\\baz", "\\\\foo\\bar\\baz"); |
| | 711 | testRelative("\\\\foo\\bar\\baz", "C:\\baz", "C:\\baz") |
| | 712 | } else { |
| | 713 | testRelative("/var/lib", "/var", ".."); |
| | 714 | testRelative("/var/lib", "/bin", "../../bin"); |
| | 715 | testRelative("/var/lib", "/var/lib", ""); |
| | 716 | testRelative("/var/lib", "/var/apache", "../apache"); |
| | 717 | testRelative("/var/", "/var/lib", "lib"); |
| | 718 | testRelative("/", "/var/lib", "var/lib"); |
| | 719 | testRelative("/foo/test", "/foo/test/bar/package.json", "bar/package.json"); |
| | 720 | testRelative("/Users/a/web/b/test/mails", "/Users/a/web/b", "../.."); |
| | 721 | testRelative("/foo/bar/baz-quux", "/foo/bar/baz", "../baz"); |
| | 722 | testRelative("/foo/bar/baz", "/foo/bar/baz-quux", "../baz-quux"); |
| | 723 | testRelative("/baz-quux", "/baz", "../baz"); |
| | 724 | testRelative("/baz", "/baz-quux", "../baz-quux"); |
| | 725 | } |
| 278 | } | 726 | } |
| 279 | fn testRelative(from: []const u8, to: []const u8, expected_output: []const u8) { | 727 | fn testRelative(from: []const u8, to: []const u8, expected_output: []const u8) { |
| 280 | const result = %%relative(&debug.global_allocator, from, to); | 728 | const result = %%relative(&debug.global_allocator, from, to); |