| ... | ... | @@ -111,6 +111,7 @@ test "os.path.isAbsoluteWindows" { |
| 111 | 111 | testIsAbsoluteWindows("C:cwd\\another", false); |
| 112 | 112 | testIsAbsoluteWindows("directory/directory", false); |
| 113 | 113 | testIsAbsoluteWindows("directory\\directory", false); |
| 114 | testIsAbsoluteWindows("/usr/local", true); |
| 114 | 115 | } |
| 115 | 116 | |
| 116 | 117 | test "os.path.isAbsolutePosix" { |
| ... | ... | @@ -128,53 +129,115 @@ fn testIsAbsolutePosix(path: []const u8, expected_result: bool) { |
| 128 | 129 | assert(isAbsolutePosix(path) == expected_result); |
| 129 | 130 | } |
| 130 | 131 | |
| 131 | | pub fn drive(path: []const u8) -> ?[]const u8 { |
| 132 | | if (path.len < 2) |
| 133 | | return null; |
| 134 | | if (path[1] != ':') |
| 135 | | return null; |
| 136 | | return path[0..2]; |
| 137 | | } |
| 132 | pub const WindowsPath = struct { |
| 133 | is_abs: bool, |
| 134 | kind: Kind, |
| 135 | disk_designator: []const u8, |
| 138 | 136 | |
| 139 | | pub fn networkShare(path: []const u8) -> ?[]const u8 { |
| 140 | | if (path.len < "//a/b".len) |
| 141 | | return null; |
| 137 | pub const Kind = enum { |
| 138 | None, |
| 139 | Drive, |
| 140 | NetworkShare, |
| 141 | }; |
| 142 | }; |
| 143 | |
| 144 | pub fn windowsParsePath(path: []const u8) -> WindowsPath { |
| 145 | if (path.len >= 2 and path[1] == ':') { |
| 146 | return WindowsPath { |
| 147 | .is_abs = isAbsoluteWindows(path), |
| 148 | .kind = WindowsPath.Kind.Drive, |
| 149 | .disk_designator = path[0..2], |
| 150 | }; |
| 151 | } |
| 152 | if (path.len >= 1 and (path[0] == '/' or path[0] == '\\') and |
| 153 | (path.len == 1 or (path[1] != '/' and path[1] != '\\'))) |
| 154 | { |
| 155 | return WindowsPath { |
| 156 | .is_abs = true, |
| 157 | .kind = WindowsPath.Kind.None, |
| 158 | .disk_designator = path[0..0], |
| 159 | }; |
| 160 | } |
| 161 | const relative_path = WindowsPath { |
| 162 | .kind = WindowsPath.Kind.None, |
| 163 | .disk_designator = []u8{}, |
| 164 | .is_abs = false, |
| 165 | }; |
| 166 | if (path.len < "//a/b".len) { |
| 167 | return relative_path; |
| 168 | } |
| 142 | 169 | |
| 143 | 170 | // TODO when I combined these together with `inline for` the compiler crashed |
| 144 | 171 | { |
| 145 | 172 | const this_sep = '/'; |
| 146 | 173 | const two_sep = []u8{this_sep, this_sep}; |
| 147 | 174 | if (mem.startsWith(u8, path, two_sep)) { |
| 148 | | if (path[2] == this_sep) |
| 149 | | return null; |
| 175 | if (path[2] == this_sep) { |
| 176 | return relative_path; |
| 177 | } |
| 150 | 178 | |
| 151 | 179 | var it = mem.split(path, []u8{this_sep}); |
| 152 | | _ = (it.next() ?? return null); |
| 153 | | _ = (it.next() ?? return null); |
| 154 | | return path[0..it.index]; |
| 180 | _ = (it.next() ?? return relative_path); |
| 181 | _ = (it.next() ?? return relative_path); |
| 182 | return WindowsPath { |
| 183 | .is_abs = isAbsoluteWindows(path), |
| 184 | .kind = WindowsPath.Kind.NetworkShare, |
| 185 | .disk_designator = path[0..it.index], |
| 186 | }; |
| 155 | 187 | } |
| 156 | 188 | } |
| 157 | 189 | { |
| 158 | 190 | const this_sep = '\\'; |
| 159 | 191 | const two_sep = []u8{this_sep, this_sep}; |
| 160 | 192 | if (mem.startsWith(u8, path, two_sep)) { |
| 161 | | if (path[2] == this_sep) |
| 162 | | return null; |
| 193 | if (path[2] == this_sep) { |
| 194 | return relative_path; |
| 195 | } |
| 163 | 196 | |
| 164 | 197 | var it = mem.split(path, []u8{this_sep}); |
| 165 | | _ = (it.next() ?? return null); |
| 166 | | _ = (it.next() ?? return null); |
| 167 | | return path[0..it.index]; |
| 198 | _ = (it.next() ?? return relative_path); |
| 199 | _ = (it.next() ?? return relative_path); |
| 200 | return WindowsPath { |
| 201 | .is_abs = isAbsoluteWindows(path), |
| 202 | .kind = WindowsPath.Kind.NetworkShare, |
| 203 | .disk_designator = path[0..it.index], |
| 204 | }; |
| 168 | 205 | } |
| 169 | 206 | } |
| 170 | | return null; |
| 207 | return relative_path; |
| 171 | 208 | } |
| 172 | 209 | |
| 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); |
| 210 | test "os.path.windowsParsePath" { |
| 211 | { |
| 212 | const parsed = windowsParsePath("//a/b"); |
| 213 | assert(parsed.is_abs); |
| 214 | assert(parsed.kind == WindowsPath.Kind.NetworkShare); |
| 215 | assert(mem.eql(u8, parsed.disk_designator, "//a/b")); |
| 216 | } |
| 217 | { |
| 218 | const parsed = windowsParsePath("\\\\a\\b"); |
| 219 | assert(parsed.is_abs); |
| 220 | assert(parsed.kind == WindowsPath.Kind.NetworkShare); |
| 221 | assert(mem.eql(u8, parsed.disk_designator, "\\\\a\\b")); |
| 222 | } |
| 223 | { |
| 224 | const parsed = windowsParsePath("\\\\a\\"); |
| 225 | assert(!parsed.is_abs); |
| 226 | assert(parsed.kind == WindowsPath.Kind.None); |
| 227 | assert(mem.eql(u8, parsed.disk_designator, "")); |
| 228 | } |
| 229 | { |
| 230 | const parsed = windowsParsePath("/usr/local"); |
| 231 | assert(parsed.is_abs); |
| 232 | assert(parsed.kind == WindowsPath.Kind.None); |
| 233 | assert(mem.eql(u8, parsed.disk_designator, "")); |
| 234 | } |
| 235 | { |
| 236 | const parsed = windowsParsePath("c:../"); |
| 237 | assert(!parsed.is_abs); |
| 238 | assert(parsed.kind == WindowsPath.Kind.Drive); |
| 239 | assert(mem.eql(u8, parsed.disk_designator, "c:")); |
| 240 | } |
| 178 | 241 | } |
| 179 | 242 | |
| 180 | 243 | pub fn diskDesignator(path: []const u8) -> []const u8 { |
| ... | ... | @@ -186,10 +249,9 @@ pub fn diskDesignator(path: []const u8) -> []const u8 { |
| 186 | 249 | } |
| 187 | 250 | |
| 188 | 251 | pub fn diskDesignatorWindows(path: []const u8) -> []const u8 { |
| 189 | | return drive(path) ?? (networkShare(path) ?? []u8{}); |
| 252 | return windowsParsePath(path).disk_designator; |
| 190 | 253 | } |
| 191 | 254 | |
| 192 | | // TODO ASCII is wrong, we actually need full unicode support to compare paths. |
| 193 | 255 | fn networkShareServersEql(ns1: []const u8, ns2: []const u8) -> bool { |
| 194 | 256 | const sep1 = ns1[0]; |
| 195 | 257 | const sep2 = ns2[0]; |
| ... | ... | @@ -197,9 +259,33 @@ fn networkShareServersEql(ns1: []const u8, ns2: []const u8) -> bool { |
| 197 | 259 | var it1 = mem.split(ns1, []u8{sep1}); |
| 198 | 260 | var it2 = mem.split(ns2, []u8{sep2}); |
| 199 | 261 | |
| 262 | // TODO ASCII is wrong, we actually need full unicode support to compare paths. |
| 200 | 263 | return asciiEqlIgnoreCase(??it1.next(), ??it2.next()); |
| 201 | 264 | } |
| 202 | 265 | |
| 266 | fn compareDiskDesignators(kind: WindowsPath.Kind, p1: []const u8, p2: []const u8) -> bool { |
| 267 | switch (kind) { |
| 268 | WindowsPath.Kind.None => { |
| 269 | assert(p1.len == 0); |
| 270 | assert(p2.len == 0); |
| 271 | return true; |
| 272 | }, |
| 273 | WindowsPath.Kind.Drive => { |
| 274 | return asciiUpper(p1[0]) == asciiUpper(p2[0]); |
| 275 | }, |
| 276 | WindowsPath.Kind.NetworkShare => { |
| 277 | const sep1 = p1[0]; |
| 278 | const sep2 = p2[0]; |
| 279 | |
| 280 | var it1 = mem.split(p1, []u8{sep1}); |
| 281 | var it2 = mem.split(p2, []u8{sep2}); |
| 282 | |
| 283 | // TODO ASCII is wrong, we actually need full unicode support to compare paths. |
| 284 | return asciiEqlIgnoreCase(??it1.next(), ??it2.next()) and asciiEqlIgnoreCase(??it1.next(), ??it2.next()); |
| 285 | }, |
| 286 | } |
| 287 | } |
| 288 | |
| 203 | 289 | fn asciiUpper(byte: u8) -> u8 { |
| 204 | 290 | return switch (byte) { |
| 205 | 291 | 'a' ... 'z' => 'A' + (byte - 'a'), |
| ... | ... | @@ -249,120 +335,157 @@ pub fn resolveWindows(allocator: &Allocator, paths: []const []const u8) -> %[]u8 |
| 249 | 335 | return os.getCwd(allocator); |
| 250 | 336 | } |
| 251 | 337 | |
| 252 | | // determine which drive we want to result with |
| 253 | | var result_drive_upcase: ?u8 = null; |
| 254 | | var have_abs = false; |
| 338 | // determine which disk designator we will result with, if any |
| 339 | var result_drive_buf = "_:"; |
| 340 | var result_disk_designator: []const u8 = ""; |
| 341 | var have_drive_kind = WindowsPath.Kind.None; |
| 342 | var have_abs_path = false; |
| 255 | 343 | var first_index: usize = 0; |
| 256 | 344 | var max_size: usize = 0; |
| 257 | 345 | for (paths) |p, i| { |
| 258 | | const is_abs = isAbsoluteWindows(p); |
| 259 | | if (is_abs) { |
| 260 | | have_abs = true; |
| 346 | const parsed = windowsParsePath(p); |
| 347 | if (parsed.is_abs) { |
| 348 | have_abs_path = true; |
| 261 | 349 | first_index = i; |
| 262 | | max_size = 0; |
| 350 | max_size = result_disk_designator.len; |
| 263 | 351 | } |
| 264 | | if (drive(p)) |d| { |
| 265 | | result_drive_upcase = asciiUpper(d[0]); |
| 266 | | } else if (networkShare(p)) |_| { |
| 267 | | result_drive_upcase = null; |
| 352 | switch (parsed.kind) { |
| 353 | WindowsPath.Kind.Drive => { |
| 354 | result_drive_buf[0] = asciiUpper(parsed.disk_designator[0]); |
| 355 | result_disk_designator = result_drive_buf[0..]; |
| 356 | have_drive_kind = WindowsPath.Kind.Drive; |
| 357 | }, |
| 358 | WindowsPath.Kind.NetworkShare => { |
| 359 | result_disk_designator = parsed.disk_designator; |
| 360 | have_drive_kind = WindowsPath.Kind.NetworkShare; |
| 361 | }, |
| 362 | WindowsPath.Kind.None => {}, |
| 268 | 363 | } |
| 269 | 364 | max_size += p.len + 1; |
| 270 | 365 | } |
| 271 | 366 | |
| 272 | 367 | |
| 273 | | // if we will result with a drive, loop again to determine |
| 274 | | // which is the first time the drive is absolutely specified, if any |
| 275 | | // and count up the max bytes for paths related to this drive |
| 276 | | if (result_drive_upcase) |res_dr| { |
| 277 | | have_abs = false; |
| 368 | // if we will result with a disk designator, loop again to determine |
| 369 | // which is the last time the disk designator is absolutely specified, if any |
| 370 | // and count up the max bytes for paths related to this disk designator |
| 371 | if (have_drive_kind != WindowsPath.Kind.None) { |
| 372 | have_abs_path = false; |
| 278 | 373 | first_index = 0; |
| 279 | | max_size = "_:".len; |
| 280 | | var correct_drive = false; |
| 374 | max_size = result_disk_designator.len; |
| 375 | var correct_disk_designator = false; |
| 281 | 376 | |
| 282 | 377 | for (paths) |p, i| { |
| 283 | | if (drive(p)) |dr| { |
| 284 | | correct_drive = asciiUpper(dr[0]) == res_dr; |
| 285 | | } else if (networkShare(p)) |_| { |
| 286 | | continue; |
| 378 | const parsed = windowsParsePath(p); |
| 379 | if (parsed.kind != WindowsPath.Kind.None) { |
| 380 | if (parsed.kind == have_drive_kind) { |
| 381 | correct_disk_designator = compareDiskDesignators(have_drive_kind, |
| 382 | result_disk_designator, parsed.disk_designator); |
| 383 | } else { |
| 384 | continue; |
| 385 | } |
| 287 | 386 | } |
| 288 | | if (!correct_drive) { |
| 387 | if (!correct_disk_designator) { |
| 289 | 388 | continue; |
| 290 | 389 | } |
| 291 | | const is_abs = isAbsoluteWindows(p); |
| 292 | | if (is_abs) { |
| 390 | if (parsed.is_abs) { |
| 293 | 391 | first_index = i; |
| 294 | | max_size = "_:".len; |
| 295 | | have_abs = true; |
| 392 | max_size = result_disk_designator.len; |
| 393 | have_abs_path = true; |
| 296 | 394 | } |
| 297 | 395 | max_size += p.len + 1; |
| 298 | 396 | } |
| 299 | 397 | } |
| 300 | 398 | |
| 301 | | var drive_buf = "_:"; |
| 399 | |
| 400 | // Allocate result and fill in the disk designator, calling getCwd if we have to. |
| 302 | 401 | var result: []u8 = undefined; |
| 303 | 402 | var result_index: usize = 0; |
| 304 | | var root_slice: []const u8 = undefined; |
| 305 | | |
| 306 | | if (have_abs) { |
| 307 | | result = %return allocator.alloc(u8, max_size); |
| 308 | | |
| 309 | | if (result_drive_upcase) |res_dr| { |
| 310 | | drive_buf[0] = res_dr; |
| 311 | | root_slice = drive_buf[0..]; |
| 312 | 403 | |
| 313 | | mem.copy(u8, result, root_slice); |
| 314 | | result_index += root_slice.len; |
| 315 | | } else { |
| 316 | | // We know it looks like //a/b or \\a\b because of earlier code |
| 317 | | var it = mem.split(paths[first_index], "/\\"); |
| 318 | | const server_name = ??it.next(); |
| 319 | | const other_name = ??it.next(); |
| 320 | | |
| 321 | | result[result_index] = '\\'; |
| 322 | | result_index += 1; |
| 323 | | result[result_index] = '\\'; |
| 324 | | result_index += 1; |
| 325 | | mem.copy(u8, result[result_index..], server_name); |
| 326 | | result_index += server_name.len; |
| 327 | | result[result_index] = '\\'; |
| 328 | | result_index += 1; |
| 329 | | mem.copy(u8, result[result_index..], other_name); |
| 330 | | result_index += other_name.len; |
| 331 | | |
| 332 | | root_slice = result[0..result_index]; |
| 404 | if (have_abs_path) { |
| 405 | switch (have_drive_kind) { |
| 406 | WindowsPath.Kind.Drive => { |
| 407 | result = %return allocator.alloc(u8, max_size); |
| 408 | |
| 409 | mem.copy(u8, result, result_disk_designator); |
| 410 | result_index += result_disk_designator.len; |
| 411 | }, |
| 412 | WindowsPath.Kind.NetworkShare => { |
| 413 | result = %return allocator.alloc(u8, max_size); |
| 414 | var it = mem.split(paths[first_index], "/\\"); |
| 415 | const server_name = ??it.next(); |
| 416 | const other_name = ??it.next(); |
| 417 | |
| 418 | result[result_index] = '\\'; |
| 419 | result_index += 1; |
| 420 | result[result_index] = '\\'; |
| 421 | result_index += 1; |
| 422 | mem.copy(u8, result[result_index..], server_name); |
| 423 | result_index += server_name.len; |
| 424 | result[result_index] = '\\'; |
| 425 | result_index += 1; |
| 426 | mem.copy(u8, result[result_index..], other_name); |
| 427 | result_index += other_name.len; |
| 428 | |
| 429 | result_disk_designator = result[0..result_index]; |
| 430 | }, |
| 431 | WindowsPath.Kind.None => { |
| 432 | assert(is_windows); // resolveWindows called on non windows can't use getCwd |
| 433 | const cwd = %return os.getCwd(allocator); |
| 434 | defer allocator.free(cwd); |
| 435 | const parsed_cwd = windowsParsePath(cwd); |
| 436 | result = %return allocator.alloc(u8, max_size + parsed_cwd.disk_designator.len + 1); |
| 437 | mem.copy(u8, result, parsed_cwd.disk_designator); |
| 438 | result_index += parsed_cwd.disk_designator.len; |
| 439 | result_disk_designator = result[0..parsed_cwd.disk_designator.len]; |
| 440 | if (parsed_cwd.kind == WindowsPath.Kind.Drive) { |
| 441 | result[0] = asciiUpper(result[0]); |
| 442 | } |
| 443 | have_drive_kind = parsed_cwd.kind; |
| 444 | }, |
| 333 | 445 | } |
| 334 | 446 | } else { |
| 335 | 447 | assert(is_windows); // resolveWindows called on non windows can't use getCwd |
| 336 | | // TODO get cwd for result_drive if applicable |
| 448 | // TODO call get cwd for the result_disk_designator instead of the global one |
| 337 | 449 | const cwd = %return os.getCwd(allocator); |
| 338 | 450 | defer allocator.free(cwd); |
| 451 | |
| 339 | 452 | result = %return allocator.alloc(u8, max_size + cwd.len + 1); |
| 453 | |
| 340 | 454 | mem.copy(u8, result, cwd); |
| 341 | 455 | result_index += cwd.len; |
| 342 | | |
| 343 | | root_slice = diskDesignatorWindows(result[0..result_index]); |
| 456 | const parsed_cwd = windowsParsePath(result[0..result_index]); |
| 457 | result_disk_designator = parsed_cwd.disk_designator; |
| 458 | if (parsed_cwd.kind == WindowsPath.Kind.Drive) { |
| 459 | result[0] = asciiUpper(result[0]); |
| 460 | } |
| 461 | have_drive_kind = parsed_cwd.kind; |
| 344 | 462 | } |
| 345 | 463 | %defer allocator.free(result); |
| 346 | 464 | |
| 347 | | var correct_drive = true; |
| 465 | // Now we know the disk designator to use, if any, and what kind it is. And our result |
| 466 | // is big enough to append all the paths to. |
| 467 | var correct_disk_designator = true; |
| 348 | 468 | for (paths[first_index..]) |p, i| { |
| 349 | | if (result_drive_upcase) |res_dr| { |
| 350 | | if (drive(p)) |dr| { |
| 351 | | correct_drive = asciiUpper(dr[0]) == res_dr; |
| 352 | | } else if (networkShare(p)) |_| { |
| 353 | | continue; |
| 354 | | } |
| 355 | | if (!correct_drive) { |
| 469 | const parsed = windowsParsePath(p); |
| 470 | |
| 471 | if (parsed.kind != WindowsPath.Kind.None) { |
| 472 | if (parsed.kind == have_drive_kind) { |
| 473 | correct_disk_designator = compareDiskDesignators(have_drive_kind, |
| 474 | result_disk_designator, parsed.disk_designator); |
| 475 | } else { |
| 356 | 476 | continue; |
| 357 | 477 | } |
| 358 | 478 | } |
| 359 | | var it = mem.split(p[diskDesignatorWindows(p).len..], "/\\"); |
| 479 | if (!correct_disk_designator) { |
| 480 | continue; |
| 481 | } |
| 482 | var it = mem.split(p[parsed.disk_designator.len..], "/\\"); |
| 360 | 483 | while (it.next()) |component| { |
| 361 | 484 | if (mem.eql(u8, component, ".")) { |
| 362 | 485 | continue; |
| 363 | 486 | } else if (mem.eql(u8, component, "..")) { |
| 364 | 487 | while (true) { |
| 365 | | if (result_index == 0 or result_index == root_slice.len) |
| 488 | if (result_index == 0 or result_index == result_disk_designator.len) |
| 366 | 489 | break; |
| 367 | 490 | result_index -= 1; |
| 368 | 491 | if (result[result_index] == '\\' or result[result_index] == '/') |
| ... | ... | @@ -377,7 +500,7 @@ pub fn resolveWindows(allocator: &Allocator, paths: []const []const u8) -> %[]u8 |
| 377 | 500 | } |
| 378 | 501 | } |
| 379 | 502 | |
| 380 | | if (result_index == root_slice.len) { |
| 503 | if (result_index == result_disk_designator.len) { |
| 381 | 504 | result[result_index] = '\\'; |
| 382 | 505 | result_index += 1; |
| 383 | 506 | } |
| ... | ... | @@ -455,6 +578,9 @@ pub fn resolvePosix(allocator: &Allocator, paths: []const []const u8) -> %[]u8 { |
| 455 | 578 | test "os.path.resolve" { |
| 456 | 579 | const cwd = %%os.getCwd(debug.global_allocator); |
| 457 | 580 | if (is_windows) { |
| 581 | if (windowsParsePath(cwd).kind == WindowsPath.Kind.Drive) { |
| 582 | cwd[0] = asciiUpper(cwd[0]); |
| 583 | } |
| 458 | 584 | assert(mem.eql(u8, testResolveWindows([][]const u8{"."}), cwd)); |
| 459 | 585 | } else { |
| 460 | 586 | assert(mem.eql(u8, testResolvePosix([][]const u8{"a/b/c/", "../../.."}), cwd)); |
| ... | ... | @@ -463,6 +589,29 @@ test "os.path.resolve" { |
| 463 | 589 | } |
| 464 | 590 | |
| 465 | 591 | test "os.path.resolveWindows" { |
| 592 | if (is_windows) { |
| 593 | const cwd = %%os.getCwd(debug.global_allocator); |
| 594 | const parsed_cwd = windowsParsePath(cwd); |
| 595 | { |
| 596 | const result = testResolveWindows([][]const u8{"/usr/local", "lib\\zig\\std\\array_list.zig"}); |
| 597 | const expected = %%join(debug.global_allocator, |
| 598 | parsed_cwd.disk_designator, "usr\\local\\lib\\zig\\std\\array_list.zig"); |
| 599 | if (parsed_cwd.kind == WindowsPath.Kind.Drive) { |
| 600 | expected[0] = asciiUpper(parsed_cwd.disk_designator[0]); |
| 601 | } |
| 602 | assert(mem.eql(u8, result, expected)); |
| 603 | } |
| 604 | { |
| 605 | const result = testResolveWindows([][]const u8{"usr/local", "lib\\zig"}); |
| 606 | const expected = %%join(debug.global_allocator, cwd, "usr\\local\\lib\\zig"); |
| 607 | if (parsed_cwd.kind == WindowsPath.Kind.Drive) { |
| 608 | expected[0] = asciiUpper(parsed_cwd.disk_designator[0]); |
| 609 | } |
| 610 | assert(mem.eql(u8, result, expected)); |
| 611 | } |
| 612 | } |
| 613 | |
| 614 | assert(mem.eql(u8, testResolveWindows([][]const u8{"c:\\a\\b\\c", "/hi", "ok"}), "C:\\hi\\ok")); |
| 466 | 615 | assert(mem.eql(u8, testResolveWindows([][]const u8{"c:/blah\\blah", "d:/games", "c:../a"}), "C:\\blah\\a")); |
| 467 | 616 | assert(mem.eql(u8, testResolveWindows([][]const u8{"c:/blah\\blah", "d:/games", "C:../a"}), "C:\\blah\\a")); |
| 468 | 617 | assert(mem.eql(u8, testResolveWindows([][]const u8{"c:/ignore", "d:\\a/b\\c/d", "\\e.exe"}), "D:\\e.exe")); |
| ... | ... | @@ -749,18 +898,21 @@ pub fn relativeWindows(allocator: &Allocator, from: []const u8, to: []const u8) |
| 749 | 898 | const resolved_to = %return resolveWindows(allocator, [][]const u8{to}); |
| 750 | 899 | defer if (clean_up_resolved_to) allocator.free(resolved_to); |
| 751 | 900 | |
| 752 | | const result_is_to = if (drive(resolved_to)) |to_drive| |
| 753 | | if (drive(resolved_from)) |from_drive| |
| 754 | | asciiUpper(from_drive[0]) != asciiUpper(to_drive[0]) |
| 755 | | else |
| 756 | | true |
| 757 | | else if (networkShare(resolved_to)) |to_ns| |
| 758 | | if (networkShare(resolved_from)) |from_ns| |
| 759 | | !networkShareServersEql(to_ns, from_ns) |
| 760 | | else |
| 761 | | true |
| 762 | | else |
| 763 | | unreachable; |
| 901 | const parsed_from = windowsParsePath(resolved_from); |
| 902 | const parsed_to = windowsParsePath(resolved_to); |
| 903 | const result_is_to = x: { |
| 904 | if (parsed_from.kind != parsed_to.kind) { |
| 905 | break :x true; |
| 906 | } else switch (parsed_from.kind) { |
| 907 | WindowsPath.Kind.NetworkShare => { |
| 908 | break :x !networkShareServersEql(parsed_to.disk_designator, parsed_from.disk_designator); |
| 909 | }, |
| 910 | WindowsPath.Kind.Drive => { |
| 911 | break :x asciiUpper(parsed_from.disk_designator[0]) != asciiUpper(parsed_to.disk_designator[0]); |
| 912 | }, |
| 913 | else => unreachable, |
| 914 | } |
| 915 | }; |
| 764 | 916 | |
| 765 | 917 | if (result_is_to) { |
| 766 | 918 | clean_up_resolved_to = false; |