| ... | @@ -111,6 +111,7 @@ test "os.path.isAbsoluteWindows" { | ... | @@ -111,6 +111,7 @@ test "os.path.isAbsoluteWindows" { |
| 111 | testIsAbsoluteWindows("C:cwd\\another", false); | 111 | testIsAbsoluteWindows("C:cwd\\another", false); |
| 112 | testIsAbsoluteWindows("directory/directory", false); | 112 | testIsAbsoluteWindows("directory/directory", false); |
| 113 | testIsAbsoluteWindows("directory\\directory", false); | 113 | testIsAbsoluteWindows("directory\\directory", false); |
| | 114 | testIsAbsoluteWindows("/usr/local", true); |
| 114 | } | 115 | } |
| 115 | | 116 | |
| 116 | test "os.path.isAbsolutePosix" { | 117 | test "os.path.isAbsolutePosix" { |
| ... | @@ -128,53 +129,115 @@ fn testIsAbsolutePosix(path: []const u8, expected_result: bool) { | ... | @@ -128,53 +129,115 @@ fn testIsAbsolutePosix(path: []const u8, expected_result: bool) { |
| 128 | assert(isAbsolutePosix(path) == expected_result); | 129 | assert(isAbsolutePosix(path) == expected_result); |
| 129 | } | 130 | } |
| 130 | | 131 | |
| 131 | pub fn drive(path: []const u8) -> ?[]const u8 { | 132 | pub const WindowsPath = struct { |
| 132 | if (path.len < 2) | 133 | is_abs: bool, |
| 133 | return null; | 134 | kind: Kind, |
| 134 | if (path[1] != ':') | 135 | disk_designator: []const u8, |
| 135 | return null; | | |
| 136 | return path[0..2]; | | |
| 137 | } | | |
| 138 | | 136 | |
| 139 | pub fn networkShare(path: []const u8) -> ?[]const u8 { | 137 | pub const Kind = enum { |
| 140 | if (path.len < "//a/b".len) | 138 | None, |
| 141 | return null; | 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 | // TODO when I combined these together with `inline for` the compiler crashed | 170 | // TODO when I combined these together with `inline for` the compiler crashed |
| 144 | { | 171 | { |
| 145 | const this_sep = '/'; | 172 | const this_sep = '/'; |
| 146 | const two_sep = []u8{this_sep, this_sep}; | 173 | const two_sep = []u8{this_sep, this_sep}; |
| 147 | if (mem.startsWith(u8, path, two_sep)) { | 174 | if (mem.startsWith(u8, path, two_sep)) { |
| 148 | if (path[2] == this_sep) | 175 | if (path[2] == this_sep) { |
| 149 | return null; | 176 | return relative_path; |
| | 177 | } |
| 150 | | 178 | |
| 151 | var it = mem.split(path, []u8{this_sep}); | 179 | var it = mem.split(path, []u8{this_sep}); |
| 152 | _ = (it.next() ?? return null); | 180 | _ = (it.next() ?? return relative_path); |
| 153 | _ = (it.next() ?? return null); | 181 | _ = (it.next() ?? return relative_path); |
| 154 | return path[0..it.index]; | 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 | const this_sep = '\\'; | 190 | const this_sep = '\\'; |
| 159 | const two_sep = []u8{this_sep, this_sep}; | 191 | const two_sep = []u8{this_sep, this_sep}; |
| 160 | if (mem.startsWith(u8, path, two_sep)) { | 192 | if (mem.startsWith(u8, path, two_sep)) { |
| 161 | if (path[2] == this_sep) | 193 | if (path[2] == this_sep) { |
| 162 | return null; | 194 | return relative_path; |
| | 195 | } |
| 163 | | 196 | |
| 164 | var it = mem.split(path, []u8{this_sep}); | 197 | var it = mem.split(path, []u8{this_sep}); |
| 165 | _ = (it.next() ?? return null); | 198 | _ = (it.next() ?? return relative_path); |
| 166 | _ = (it.next() ?? return null); | 199 | _ = (it.next() ?? return relative_path); |
| 167 | return path[0..it.index]; | 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" { | 210 | test "os.path.windowsParsePath" { |
| 174 | assert(mem.eql(u8, ??networkShare("//a/b"), "//a/b")); | 211 | { |
| 175 | assert(mem.eql(u8, ??networkShare("\\\\a\\b"), "\\\\a\\b")); | 212 | const parsed = windowsParsePath("//a/b"); |
| 176 | | 213 | assert(parsed.is_abs); |
| 177 | assert(networkShare("\\\\a\\") == null); | 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 | pub fn diskDesignator(path: []const u8) -> []const u8 { | 243 | pub fn diskDesignator(path: []const u8) -> []const u8 { |
| ... | @@ -186,10 +249,9 @@ 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 | pub fn diskDesignatorWindows(path: []const u8) -> []const u8 { | 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 | fn networkShareServersEql(ns1: []const u8, ns2: []const u8) -> bool { | 255 | fn networkShareServersEql(ns1: []const u8, ns2: []const u8) -> bool { |
| 194 | const sep1 = ns1[0]; | 256 | const sep1 = ns1[0]; |
| 195 | const sep2 = ns2[0]; | 257 | const sep2 = ns2[0]; |
| ... | @@ -197,9 +259,33 @@ fn networkShareServersEql(ns1: []const u8, ns2: []const u8) -> bool { | ... | @@ -197,9 +259,33 @@ fn networkShareServersEql(ns1: []const u8, ns2: []const u8) -> bool { |
| 197 | var it1 = mem.split(ns1, []u8{sep1}); | 259 | var it1 = mem.split(ns1, []u8{sep1}); |
| 198 | var it2 = mem.split(ns2, []u8{sep2}); | 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 | return asciiEqlIgnoreCase(??it1.next(), ??it2.next()); | 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 | fn asciiUpper(byte: u8) -> u8 { | 289 | fn asciiUpper(byte: u8) -> u8 { |
| 204 | return switch (byte) { | 290 | return switch (byte) { |
| 205 | 'a' ... 'z' => 'A' + (byte - 'a'), | 291 | 'a' ... 'z' => 'A' + (byte - 'a'), |
| ... | @@ -249,120 +335,157 @@ pub fn resolveWindows(allocator: &Allocator, paths: []const []const u8) -> %[]u8 | ... | @@ -249,120 +335,157 @@ pub fn resolveWindows(allocator: &Allocator, paths: []const []const u8) -> %[]u8 |
| 249 | return os.getCwd(allocator); | 335 | return os.getCwd(allocator); |
| 250 | } | 336 | } |
| 251 | | 337 | |
| 252 | // determine which drive we want to result with | 338 | // determine which disk designator we will result with, if any |
| 253 | var result_drive_upcase: ?u8 = null; | 339 | var result_drive_buf = "_:"; |
| 254 | var have_abs = false; | 340 | var result_disk_designator: []const u8 = ""; |
| | 341 | var have_drive_kind = WindowsPath.Kind.None; |
| | 342 | var have_abs_path = false; |
| 255 | var first_index: usize = 0; | 343 | var first_index: usize = 0; |
| 256 | var max_size: usize = 0; | 344 | var max_size: usize = 0; |
| 257 | for (paths) |p, i| { | 345 | for (paths) |p, i| { |
| 258 | const is_abs = isAbsoluteWindows(p); | 346 | const parsed = windowsParsePath(p); |
| 259 | if (is_abs) { | 347 | if (parsed.is_abs) { |
| 260 | have_abs = true; | 348 | have_abs_path = true; |
| 261 | first_index = i; | 349 | first_index = i; |
| 262 | max_size = 0; | 350 | max_size = result_disk_designator.len; |
| 263 | } | 351 | } |
| 264 | if (drive(p)) |d| { | 352 | switch (parsed.kind) { |
| 265 | result_drive_upcase = asciiUpper(d[0]); | 353 | WindowsPath.Kind.Drive => { |
| 266 | } else if (networkShare(p)) |_| { | 354 | result_drive_buf[0] = asciiUpper(parsed.disk_designator[0]); |
| 267 | result_drive_upcase = null; | 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 | max_size += p.len + 1; | 364 | max_size += p.len + 1; |
| 270 | } | 365 | } |
| 271 | | 366 | |
| 272 | | 367 | |
| 273 | // if we will result with a drive, loop again to determine | 368 | // if we will result with a disk designator, loop again to determine |
| 274 | // which is the first time the drive is absolutely specified, if any | 369 | // which is the last time the disk designator is absolutely specified, if any |
| 275 | // and count up the max bytes for paths related to this drive | 370 | // and count up the max bytes for paths related to this disk designator |
| 276 | if (result_drive_upcase) |res_dr| { | 371 | if (have_drive_kind != WindowsPath.Kind.None) { |
| 277 | have_abs = false; | 372 | have_abs_path = false; |
| 278 | first_index = 0; | 373 | first_index = 0; |
| 279 | max_size = "_:".len; | 374 | max_size = result_disk_designator.len; |
| 280 | var correct_drive = false; | 375 | var correct_disk_designator = false; |
| 281 | | 376 | |
| 282 | for (paths) |p, i| { | 377 | for (paths) |p, i| { |
| 283 | if (drive(p)) |dr| { | 378 | const parsed = windowsParsePath(p); |
| 284 | correct_drive = asciiUpper(dr[0]) == res_dr; | 379 | if (parsed.kind != WindowsPath.Kind.None) { |
| 285 | } else if (networkShare(p)) |_| { | 380 | if (parsed.kind == have_drive_kind) { |
| 286 | continue; | 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 | continue; | 388 | continue; |
| 290 | } | 389 | } |
| 291 | const is_abs = isAbsoluteWindows(p); | 390 | if (parsed.is_abs) { |
| 292 | if (is_abs) { | | |
| 293 | first_index = i; | 391 | first_index = i; |
| 294 | max_size = "_:".len; | 392 | max_size = result_disk_designator.len; |
| 295 | have_abs = true; | 393 | have_abs_path = true; |
| 296 | } | 394 | } |
| 297 | max_size += p.len + 1; | 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 | var result: []u8 = undefined; | 401 | var result: []u8 = undefined; |
| 303 | var result_index: usize = 0; | 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); | 404 | if (have_abs_path) { |
| 314 | result_index += root_slice.len; | 405 | switch (have_drive_kind) { |
| 315 | } else { | 406 | WindowsPath.Kind.Drive => { |
| 316 | // We know it looks like //a/b or \\a\b because of earlier code | 407 | result = %return allocator.alloc(u8, max_size); |
| 317 | var it = mem.split(paths[first_index], "/\\"); | 408 | |
| 318 | const server_name = ??it.next(); | 409 | mem.copy(u8, result, result_disk_designator); |
| 319 | const other_name = ??it.next(); | 410 | result_index += result_disk_designator.len; |
| 320 | | 411 | }, |
| 321 | result[result_index] = '\\'; | 412 | WindowsPath.Kind.NetworkShare => { |
| 322 | result_index += 1; | 413 | result = %return allocator.alloc(u8, max_size); |
| 323 | result[result_index] = '\\'; | 414 | var it = mem.split(paths[first_index], "/\\"); |
| 324 | result_index += 1; | 415 | const server_name = ??it.next(); |
| 325 | mem.copy(u8, result[result_index..], server_name); | 416 | const other_name = ??it.next(); |
| 326 | result_index += server_name.len; | 417 | |
| 327 | result[result_index] = '\\'; | 418 | result[result_index] = '\\'; |
| 328 | result_index += 1; | 419 | result_index += 1; |
| 329 | mem.copy(u8, result[result_index..], other_name); | 420 | result[result_index] = '\\'; |
| 330 | result_index += other_name.len; | 421 | result_index += 1; |
| 331 | | 422 | mem.copy(u8, result[result_index..], server_name); |
| 332 | root_slice = result[0..result_index]; | 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 | } else { | 446 | } else { |
| 335 | assert(is_windows); // resolveWindows called on non windows can't use getCwd | 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 | const cwd = %return os.getCwd(allocator); | 449 | const cwd = %return os.getCwd(allocator); |
| 338 | defer allocator.free(cwd); | 450 | defer allocator.free(cwd); |
| | 451 | |
| 339 | result = %return allocator.alloc(u8, max_size + cwd.len + 1); | 452 | result = %return allocator.alloc(u8, max_size + cwd.len + 1); |
| | 453 | |
| 340 | mem.copy(u8, result, cwd); | 454 | mem.copy(u8, result, cwd); |
| 341 | result_index += cwd.len; | 455 | result_index += cwd.len; |
| 342 | | 456 | const parsed_cwd = windowsParsePath(result[0..result_index]); |
| 343 | root_slice = diskDesignatorWindows(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 | %defer allocator.free(result); | 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 | for (paths[first_index..]) |p, i| { | 468 | for (paths[first_index..]) |p, i| { |
| 349 | if (result_drive_upcase) |res_dr| { | 469 | const parsed = windowsParsePath(p); |
| 350 | if (drive(p)) |dr| { | 470 | |
| 351 | correct_drive = asciiUpper(dr[0]) == res_dr; | 471 | if (parsed.kind != WindowsPath.Kind.None) { |
| 352 | } else if (networkShare(p)) |_| { | 472 | if (parsed.kind == have_drive_kind) { |
| 353 | continue; | 473 | correct_disk_designator = compareDiskDesignators(have_drive_kind, |
| 354 | } | 474 | result_disk_designator, parsed.disk_designator); |
| 355 | if (!correct_drive) { | 475 | } else { |
| 356 | continue; | 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 | while (it.next()) |component| { | 483 | while (it.next()) |component| { |
| 361 | if (mem.eql(u8, component, ".")) { | 484 | if (mem.eql(u8, component, ".")) { |
| 362 | continue; | 485 | continue; |
| 363 | } else if (mem.eql(u8, component, "..")) { | 486 | } else if (mem.eql(u8, component, "..")) { |
| 364 | while (true) { | 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 | break; | 489 | break; |
| 367 | result_index -= 1; | 490 | result_index -= 1; |
| 368 | if (result[result_index] == '\\' or result[result_index] == '/') | 491 | if (result[result_index] == '\\' or result[result_index] == '/') |
| ... | @@ -377,7 +500,7 @@ pub fn resolveWindows(allocator: &Allocator, paths: []const []const u8) -> %[]u8 | ... | @@ -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 | result[result_index] = '\\'; | 504 | result[result_index] = '\\'; |
| 382 | result_index += 1; | 505 | result_index += 1; |
| 383 | } | 506 | } |
| ... | @@ -455,6 +578,9 @@ pub fn resolvePosix(allocator: &Allocator, paths: []const []const u8) -> %[]u8 { | ... | @@ -455,6 +578,9 @@ pub fn resolvePosix(allocator: &Allocator, paths: []const []const u8) -> %[]u8 { |
| 455 | test "os.path.resolve" { | 578 | test "os.path.resolve" { |
| 456 | const cwd = %%os.getCwd(debug.global_allocator); | 579 | const cwd = %%os.getCwd(debug.global_allocator); |
| 457 | if (is_windows) { | 580 | if (is_windows) { |
| | 581 | if (windowsParsePath(cwd).kind == WindowsPath.Kind.Drive) { |
| | 582 | cwd[0] = asciiUpper(cwd[0]); |
| | 583 | } |
| 458 | assert(mem.eql(u8, testResolveWindows([][]const u8{"."}), cwd)); | 584 | assert(mem.eql(u8, testResolveWindows([][]const u8{"."}), cwd)); |
| 459 | } else { | 585 | } else { |
| 460 | assert(mem.eql(u8, testResolvePosix([][]const u8{"a/b/c/", "../../.."}), cwd)); | 586 | assert(mem.eql(u8, testResolvePosix([][]const u8{"a/b/c/", "../../.."}), cwd)); |
| ... | @@ -463,6 +589,29 @@ test "os.path.resolve" { | ... | @@ -463,6 +589,29 @@ test "os.path.resolve" { |
| 463 | } | 589 | } |
| 464 | | 590 | |
| 465 | test "os.path.resolveWindows" { | 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 | assert(mem.eql(u8, testResolveWindows([][]const u8{"c:/blah\\blah", "d:/games", "c:../a"}), "C:\\blah\\a")); | 615 | assert(mem.eql(u8, testResolveWindows([][]const u8{"c:/blah\\blah", "d:/games", "c:../a"}), "C:\\blah\\a")); |
| 467 | assert(mem.eql(u8, testResolveWindows([][]const u8{"c:/blah\\blah", "d:/games", "C:../a"}), "C:\\blah\\a")); | 616 | assert(mem.eql(u8, testResolveWindows([][]const u8{"c:/blah\\blah", "d:/games", "C:../a"}), "C:\\blah\\a")); |
| 468 | assert(mem.eql(u8, testResolveWindows([][]const u8{"c:/ignore", "d:\\a/b\\c/d", "\\e.exe"}), "D:\\e.exe")); | 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,18 +898,21 @@ pub fn relativeWindows(allocator: &Allocator, from: []const u8, to: []const u8) |
| 749 | const resolved_to = %return resolveWindows(allocator, [][]const u8{to}); | 898 | const resolved_to = %return resolveWindows(allocator, [][]const u8{to}); |
| 750 | defer if (clean_up_resolved_to) allocator.free(resolved_to); | 899 | defer if (clean_up_resolved_to) allocator.free(resolved_to); |
| 751 | | 900 | |
| 752 | const result_is_to = if (drive(resolved_to)) |to_drive| | 901 | const parsed_from = windowsParsePath(resolved_from); |
| 753 | if (drive(resolved_from)) |from_drive| | 902 | const parsed_to = windowsParsePath(resolved_to); |
| 754 | asciiUpper(from_drive[0]) != asciiUpper(to_drive[0]) | 903 | const result_is_to = x: { |
| 755 | else | 904 | if (parsed_from.kind != parsed_to.kind) { |
| 756 | true | 905 | break :x true; |
| 757 | else if (networkShare(resolved_to)) |to_ns| | 906 | } else switch (parsed_from.kind) { |
| 758 | if (networkShare(resolved_from)) |from_ns| | 907 | WindowsPath.Kind.NetworkShare => { |
| 759 | !networkShareServersEql(to_ns, from_ns) | 908 | break :x !networkShareServersEql(parsed_to.disk_designator, parsed_from.disk_designator); |
| 760 | else | 909 | }, |
| 761 | true | 910 | WindowsPath.Kind.Drive => { |
| 762 | else | 911 | break :x asciiUpper(parsed_from.disk_designator[0]) != asciiUpper(parsed_to.disk_designator[0]); |
| 763 | unreachable; | 912 | }, |
| | 913 | else => unreachable, |
| | 914 | } |
| | 915 | }; |
| 764 | | 916 | |
| 765 | if (result_is_to) { | 917 | if (result_is_to) { |
| 766 | clean_up_resolved_to = false; | 918 | clean_up_resolved_to = false; |