| ... | @@ -195,12 +195,15 @@ pub fn rootPosix(path: []const u8) -> []const u8 { | ... | @@ -195,12 +195,15 @@ pub fn rootPosix(path: []const u8) -> []const u8 { |
| 195 | return path[0..1]; | 195 | return path[0..1]; |
| 196 | } | 196 | } |
| 197 | | 197 | |
| 198 | pub fn drivesEqual(drive1: []const u8, drive2: []const u8) -> bool { | 198 | // TODO ASCII is wrong, we actually need full unicode support to compare paths. |
| 199 | assert(drive1.len == 2); | 199 | fn networkShareServersEql(ns1: []const u8, ns2: []const u8) -> bool { |
| 200 | assert(drive2.len == 2); | 200 | const sep1 = ns1[0]; |
| 201 | assert(drive1[1] == ':'); | 201 | const sep2 = ns2[0]; |
| 202 | assert(drive2[1] == ':'); | 202 | |
| 203 | return asciiUpper(drive1[0]) == asciiUpper(drive2[0]); | 203 | var it1 = mem.split(ns1, []u8{sep1}); |
| | 204 | var it2 = mem.split(ns2, []u8{sep2}); |
| | 205 | |
| | 206 | return asciiEqlIgnoreCase(??it1.next(), ??it2.next()); |
| 204 | } | 207 | } |
| 205 | | 208 | |
| 206 | fn asciiUpper(byte: u8) -> u8 { | 209 | fn asciiUpper(byte: u8) -> u8 { |
| ... | @@ -210,6 +213,17 @@ fn asciiUpper(byte: u8) -> u8 { | ... | @@ -210,6 +213,17 @@ fn asciiUpper(byte: u8) -> u8 { |
| 210 | }; | 213 | }; |
| 211 | } | 214 | } |
| 212 | | 215 | |
| | 216 | fn asciiEqlIgnoreCase(s1: []const u8, s2: []const u8) -> bool { |
| | 217 | if (s1.len != s2.len) |
| | 218 | return false; |
| | 219 | var i: usize = 0; |
| | 220 | while (i < s1.len) : (i += 1) { |
| | 221 | if (asciiUpper(s1[i]) != asciiUpper(s2[i])) |
| | 222 | return false; |
| | 223 | } |
| | 224 | return true; |
| | 225 | } |
| | 226 | |
| 213 | /// Converts the command line arguments into a slice and calls `resolveSlice`. | 227 | /// Converts the command line arguments into a slice and calls `resolveSlice`. |
| 214 | pub fn resolve(allocator: &Allocator, args: ...) -> %[]u8 { | 228 | pub fn resolve(allocator: &Allocator, args: ...) -> %[]u8 { |
| 215 | var paths: [args.len][]const u8 = undefined; | 229 | var paths: [args.len][]const u8 = undefined; |
| ... | @@ -721,26 +735,95 @@ fn testBasenameWindows(input: []const u8, expected_output: []const u8) { | ... | @@ -721,26 +735,95 @@ fn testBasenameWindows(input: []const u8, expected_output: []const u8) { |
| 721 | assert(mem.eql(u8, basenameWindows(input), expected_output)); | 735 | assert(mem.eql(u8, basenameWindows(input), expected_output)); |
| 722 | } | 736 | } |
| 723 | | 737 | |
| 724 | /// Returns the relative path from ::from to ::to. If ::from and ::to each | 738 | /// Returns the relative path from `from` to `to`. If `from` and `to` each |
| 725 | /// resolve to the same path (after calling ::resolve on each), a zero-length | 739 | /// resolve to the same path (after calling `resolve` on each), a zero-length |
| 726 | /// string is returned. | 740 | /// string is returned. |
| | 741 | /// On Windows this canonicalizes the drive to a capital letter and paths to `\\`. |
| 727 | pub fn relative(allocator: &Allocator, from: []const u8, to: []const u8) -> %[]u8 { | 742 | pub fn relative(allocator: &Allocator, from: []const u8, to: []const u8) -> %[]u8 { |
| 728 | if (is_windows) { | 743 | if (is_windows) { |
| 729 | return windowsRelative(allocator, from, to); | 744 | return relativeWindows(allocator, from, to); |
| 730 | } else { | 745 | } else { |
| 731 | return posixRelative(allocator, from, to); | 746 | return relativePosix(allocator, from, to); |
| 732 | } | 747 | } |
| 733 | } | 748 | } |
| 734 | | 749 | |
| 735 | fn windowsRelative(allocator: &Allocator, from: []const u8, to: []const u8) -> %[]u8 { | 750 | pub fn relativeWindows(allocator: &Allocator, from: []const u8, to: []const u8) -> %[]u8 { |
| 736 | @compileError("TODO implement this"); | 751 | const resolved_from = %return resolveWindows(allocator, [][]const u8{from}); |
| | 752 | defer allocator.free(resolved_from); |
| | 753 | |
| | 754 | var clean_up_resolved_to = true; |
| | 755 | const resolved_to = %return resolveWindows(allocator, [][]const u8{to}); |
| | 756 | defer if (clean_up_resolved_to) allocator.free(resolved_to); |
| | 757 | |
| | 758 | const result_is_to = if (drive(resolved_to)) |to_drive| { |
| | 759 | if (drive(resolved_from)) |from_drive| { |
| | 760 | asciiUpper(from_drive[0]) != asciiUpper(to_drive[0]) |
| | 761 | } else { |
| | 762 | true |
| | 763 | } |
| | 764 | } else if (networkShare(resolved_to)) |to_ns| { |
| | 765 | if (networkShare(resolved_from)) |from_ns| { |
| | 766 | !networkShareServersEql(to_ns, from_ns) |
| | 767 | } else { |
| | 768 | true |
| | 769 | } |
| | 770 | } else { |
| | 771 | unreachable |
| | 772 | }; |
| | 773 | if (result_is_to) { |
| | 774 | clean_up_resolved_to = false; |
| | 775 | return resolved_to; |
| | 776 | } |
| | 777 | |
| | 778 | var from_it = mem.split(resolved_from, "/\\"); |
| | 779 | var to_it = mem.split(resolved_to, "/\\"); |
| | 780 | while (true) { |
| | 781 | const from_component = from_it.next() ?? return mem.dupe(allocator, u8, to_it.rest()); |
| | 782 | const to_rest = to_it.rest(); |
| | 783 | if (to_it.next()) |to_component| { |
| | 784 | // TODO ASCII is wrong, we actually need full unicode support to compare paths. |
| | 785 | if (asciiEqlIgnoreCase(from_component, to_component)) |
| | 786 | continue; |
| | 787 | } |
| | 788 | var up_count: usize = 1; |
| | 789 | while (from_it.next()) |_| { |
| | 790 | up_count += 1; |
| | 791 | } |
| | 792 | const up_index_end = up_count * "..\\".len; |
| | 793 | const result = %return allocator.alloc(u8, up_index_end + to_rest.len); |
| | 794 | %defer allocator.free(result); |
| | 795 | |
| | 796 | var result_index: usize = 0; |
| | 797 | while (result_index < up_index_end) { |
| | 798 | result[result_index] = '.'; |
| | 799 | result_index += 1; |
| | 800 | result[result_index] = '.'; |
| | 801 | result_index += 1; |
| | 802 | result[result_index] = '\\'; |
| | 803 | result_index += 1; |
| | 804 | } |
| | 805 | // shave off the trailing slash |
| | 806 | result_index -= 1; |
| | 807 | |
| | 808 | var rest_it = mem.split(to_rest, "/\\"); |
| | 809 | while (rest_it.next()) |to_component| { |
| | 810 | result[result_index] = '\\'; |
| | 811 | result_index += 1; |
| | 812 | mem.copy(u8, result[result_index..], to_component); |
| | 813 | result_index += to_component.len; |
| | 814 | } |
| | 815 | |
| | 816 | return result[0..result_index]; |
| | 817 | } |
| | 818 | |
| | 819 | return []u8{}; |
| 737 | } | 820 | } |
| 738 | | 821 | |
| 739 | fn posixRelative(allocator: &Allocator, from: []const u8, to: []const u8) -> %[]u8 { | 822 | pub fn relativePosix(allocator: &Allocator, from: []const u8, to: []const u8) -> %[]u8 { |
| 740 | const resolved_from = %return resolve(allocator, from); | 823 | const resolved_from = %return resolvePosix(allocator, [][]const u8{from}); |
| 741 | defer allocator.free(resolved_from); | 824 | defer allocator.free(resolved_from); |
| 742 | | 825 | |
| 743 | const resolved_to = %return resolve(allocator, to); | 826 | const resolved_to = %return resolvePosix(allocator, [][]const u8{to}); |
| 744 | defer allocator.free(resolved_to); | 827 | defer allocator.free(resolved_to); |
| 745 | | 828 | |
| 746 | var from_it = mem.split(resolved_from, "/"); | 829 | var from_it = mem.split(resolved_from, "/"); |
| ... | @@ -782,48 +865,52 @@ fn posixRelative(allocator: &Allocator, from: []const u8, to: []const u8) -> %[] | ... | @@ -782,48 +865,52 @@ fn posixRelative(allocator: &Allocator, from: []const u8, to: []const u8) -> %[] |
| 782 | } | 865 | } |
| 783 | | 866 | |
| 784 | test "os.path.relative" { | 867 | test "os.path.relative" { |
| 785 | if (is_windows) { | 868 | testRelativeWindows("c:/blah\\blah", "d:/games", "D:\\games"); |
| 786 | testRelative("c:/blah\\blah", "d:/games", "d:\\games"); | 869 | testRelativeWindows("c:/aaaa/bbbb", "c:/aaaa", ".."); |
| 787 | testRelative("c:/aaaa/bbbb", "c:/aaaa", ".."); | 870 | testRelativeWindows("c:/aaaa/bbbb", "c:/cccc", "..\\..\\cccc"); |
| 788 | testRelative("c:/aaaa/bbbb", "c:/cccc", "..\\..\\cccc"); | 871 | testRelativeWindows("c:/aaaa/bbbb", "c:/aaaa/bbbb", ""); |
| 789 | testRelative("c:/aaaa/bbbb", "c:/aaaa/bbbb", ""); | 872 | testRelativeWindows("c:/aaaa/bbbb", "c:/aaaa/cccc", "..\\cccc"); |
| 790 | testRelative("c:/aaaa/bbbb", "c:/aaaa/cccc", "..\\cccc"); | 873 | testRelativeWindows("c:/aaaa/", "c:/aaaa/cccc", "cccc"); |
| 791 | testRelative("c:/aaaa/", "c:/aaaa/cccc", "cccc"); | 874 | testRelativeWindows("c:/", "c:\\aaaa\\bbbb", "aaaa\\bbbb"); |
| 792 | testRelative("c:/", "c:\\aaaa\\bbbb", "aaaa\\bbbb"); | 875 | testRelativeWindows("c:/aaaa/bbbb", "d:\\", "D:\\"); |
| 793 | testRelative("c:/aaaa/bbbb", "d:\\", "d:\\"); | 876 | testRelativeWindows("c:/AaAa/bbbb", "c:/aaaa/bbbb", ""); |
| 794 | testRelative("c:/AaAa/bbbb", "c:/aaaa/bbbb", ""); | 877 | testRelativeWindows("c:/aaaaa/", "c:/aaaa/cccc", "..\\aaaa\\cccc"); |
| 795 | testRelative("c:/aaaaa/", "c:/aaaa/cccc", "..\\aaaa\\cccc"); | 878 | testRelativeWindows("C:\\foo\\bar\\baz\\quux", "C:\\", "..\\..\\..\\.."); |
| 796 | testRelative("C:\\foo\\bar\\baz\\quux", "C:\\", "..\\..\\..\\.."); | 879 | testRelativeWindows("C:\\foo\\test", "C:\\foo\\test\\bar\\package.json", "bar\\package.json"); |
| 797 | testRelative("C:\\foo\\test", "C:\\foo\\test\\bar\\package.json", "bar\\package.json"); | 880 | testRelativeWindows("C:\\foo\\bar\\baz-quux", "C:\\foo\\bar\\baz", "..\\baz"); |
| 798 | testRelative("C:\\foo\\bar\\baz-quux", "C:\\foo\\bar\\baz", "..\\baz"); | 881 | testRelativeWindows("C:\\foo\\bar\\baz", "C:\\foo\\bar\\baz-quux", "..\\baz-quux"); |
| 799 | testRelative("C:\\foo\\bar\\baz", "C:\\foo\\bar\\baz-quux", "..\\baz-quux"); | 882 | testRelativeWindows("\\\\foo\\bar", "\\\\foo\\bar\\baz", "baz"); |
| 800 | testRelative("\\\\foo\\bar", "\\\\foo\\bar\\baz", "baz"); | 883 | testRelativeWindows("\\\\foo\\bar\\baz", "\\\\foo\\bar", ".."); |
| 801 | testRelative("\\\\foo\\bar\\baz", "\\\\foo\\bar", ".."); | 884 | testRelativeWindows("\\\\foo\\bar\\baz-quux", "\\\\foo\\bar\\baz", "..\\baz"); |
| 802 | testRelative("\\\\foo\\bar\\baz-quux", "\\\\foo\\bar\\baz", "..\\baz"); | 885 | testRelativeWindows("\\\\foo\\bar\\baz", "\\\\foo\\bar\\baz-quux", "..\\baz-quux"); |
| 803 | testRelative("\\\\foo\\bar\\baz", "\\\\foo\\bar\\baz-quux", "..\\baz-quux"); | 886 | testRelativeWindows("C:\\baz-quux", "C:\\baz", "..\\baz"); |
| 804 | testRelative("C:\\baz-quux", "C:\\baz", "..\\baz"); | 887 | testRelativeWindows("C:\\baz", "C:\\baz-quux", "..\\baz-quux"); |
| 805 | testRelative("C:\\baz", "C:\\baz-quux", "..\\baz-quux"); | 888 | testRelativeWindows("\\\\foo\\baz-quux", "\\\\foo\\baz", "..\\baz"); |
| 806 | testRelative("\\\\foo\\baz-quux", "\\\\foo\\baz", "..\\baz"); | 889 | testRelativeWindows("\\\\foo\\baz", "\\\\foo\\baz-quux", "..\\baz-quux"); |
| 807 | testRelative("\\\\foo\\baz", "\\\\foo\\baz-quux", "..\\baz-quux"); | 890 | testRelativeWindows("C:\\baz", "\\\\foo\\bar\\baz", "\\\\foo\\bar\\baz"); |
| 808 | testRelative("C:\\baz", "\\\\foo\\bar\\baz", "\\\\foo\\bar\\baz"); | 891 | testRelativeWindows("\\\\foo\\bar\\baz", "C:\\baz", "C:\\baz"); |
| 809 | testRelative("\\\\foo\\bar\\baz", "C:\\baz", "C:\\baz") | 892 | |
| 810 | } else { | 893 | testRelativePosix("/var/lib", "/var", ".."); |
| 811 | testRelative("/var/lib", "/var", ".."); | 894 | testRelativePosix("/var/lib", "/bin", "../../bin"); |
| 812 | testRelative("/var/lib", "/bin", "../../bin"); | 895 | testRelativePosix("/var/lib", "/var/lib", ""); |
| 813 | testRelative("/var/lib", "/var/lib", ""); | 896 | testRelativePosix("/var/lib", "/var/apache", "../apache"); |
| 814 | testRelative("/var/lib", "/var/apache", "../apache"); | 897 | testRelativePosix("/var/", "/var/lib", "lib"); |
| 815 | testRelative("/var/", "/var/lib", "lib"); | 898 | testRelativePosix("/", "/var/lib", "var/lib"); |
| 816 | testRelative("/", "/var/lib", "var/lib"); | 899 | testRelativePosix("/foo/test", "/foo/test/bar/package.json", "bar/package.json"); |
| 817 | testRelative("/foo/test", "/foo/test/bar/package.json", "bar/package.json"); | 900 | testRelativePosix("/Users/a/web/b/test/mails", "/Users/a/web/b", "../.."); |
| 818 | testRelative("/Users/a/web/b/test/mails", "/Users/a/web/b", "../.."); | 901 | testRelativePosix("/foo/bar/baz-quux", "/foo/bar/baz", "../baz"); |
| 819 | testRelative("/foo/bar/baz-quux", "/foo/bar/baz", "../baz"); | 902 | testRelativePosix("/foo/bar/baz", "/foo/bar/baz-quux", "../baz-quux"); |
| 820 | testRelative("/foo/bar/baz", "/foo/bar/baz-quux", "../baz-quux"); | 903 | testRelativePosix("/baz-quux", "/baz", "../baz"); |
| 821 | testRelative("/baz-quux", "/baz", "../baz"); | 904 | testRelativePosix("/baz", "/baz-quux", "../baz-quux"); |
| 822 | testRelative("/baz", "/baz-quux", "../baz-quux"); | 905 | } |
| 823 | } | 906 | |
| 824 | } | 907 | fn testRelativePosix(from: []const u8, to: []const u8, expected_output: []const u8) { |
| 825 | fn testRelative(from: []const u8, to: []const u8, expected_output: []const u8) { | 908 | const result = %%relativePosix(&debug.global_allocator, from, to); |
| 826 | const result = %%relative(&debug.global_allocator, from, to); | 909 | assert(mem.eql(u8, result, expected_output)); |
| | 910 | } |
| | 911 | |
| | 912 | fn testRelativeWindows(from: []const u8, to: []const u8, expected_output: []const u8) { |
| | 913 | const result = %%relativeWindows(&debug.global_allocator, from, to); |
| 827 | assert(mem.eql(u8, result, expected_output)); | 914 | assert(mem.eql(u8, result, expected_output)); |
| 828 | } | 915 | } |
| 829 | | 916 | |