| ... | ... | @@ -195,12 +195,15 @@ pub fn rootPosix(path: []const u8) -> []const u8 { |
| 195 | 195 | return path[0..1]; |
| 196 | 196 | } |
| 197 | 197 | |
| 198 | | pub fn drivesEqual(drive1: []const u8, drive2: []const u8) -> bool { |
| 199 | | assert(drive1.len == 2); |
| 200 | | assert(drive2.len == 2); |
| 201 | | assert(drive1[1] == ':'); |
| 202 | | assert(drive2[1] == ':'); |
| 203 | | return asciiUpper(drive1[0]) == asciiUpper(drive2[0]); |
| 198 | // TODO ASCII is wrong, we actually need full unicode support to compare paths. |
| 199 | fn networkShareServersEql(ns1: []const u8, ns2: []const u8) -> bool { |
| 200 | const sep1 = ns1[0]; |
| 201 | const sep2 = ns2[0]; |
| 202 | |
| 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 | 209 | 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 | 227 | /// Converts the command line arguments into a slice and calls `resolveSlice`. |
| 214 | 228 | pub fn resolve(allocator: &Allocator, args: ...) -> %[]u8 { |
| 215 | 229 | var paths: [args.len][]const u8 = undefined; |
| ... | ... | @@ -721,26 +735,95 @@ fn testBasenameWindows(input: []const u8, expected_output: []const u8) { |
| 721 | 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 |
| 725 | | /// resolve to the same path (after calling ::resolve on each), a zero-length |
| 738 | /// Returns the relative path from `from` to `to`. If `from` and `to` each |
| 739 | /// resolve to the same path (after calling `resolve` on each), a zero-length |
| 726 | 740 | /// string is returned. |
| 741 | /// On Windows this canonicalizes the drive to a capital letter and paths to `\\`. |
| 727 | 742 | pub fn relative(allocator: &Allocator, from: []const u8, to: []const u8) -> %[]u8 { |
| 728 | 743 | if (is_windows) { |
| 729 | | return windowsRelative(allocator, from, to); |
| 744 | return relativeWindows(allocator, from, to); |
| 730 | 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 { |
| 736 | | @compileError("TODO implement this"); |
| 750 | pub fn relativeWindows(allocator: &Allocator, from: []const u8, to: []const u8) -> %[]u8 { |
| 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 { |
| 740 | | const resolved_from = %return resolve(allocator, from); |
| 822 | pub fn relativePosix(allocator: &Allocator, from: []const u8, to: []const u8) -> %[]u8 { |
| 823 | const resolved_from = %return resolvePosix(allocator, [][]const u8{from}); |
| 741 | 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 | 827 | defer allocator.free(resolved_to); |
| 745 | 828 | |
| 746 | 829 | var from_it = mem.split(resolved_from, "/"); |
| ... | ... | @@ -782,48 +865,52 @@ fn posixRelative(allocator: &Allocator, from: []const u8, to: []const u8) -> %[] |
| 782 | 865 | } |
| 783 | 866 | |
| 784 | 867 | test "os.path.relative" { |
| 785 | | if (is_windows) { |
| 786 | | testRelative("c:/blah\\blah", "d:/games", "d:\\games"); |
| 787 | | testRelative("c:/aaaa/bbbb", "c:/aaaa", ".."); |
| 788 | | testRelative("c:/aaaa/bbbb", "c:/cccc", "..\\..\\cccc"); |
| 789 | | testRelative("c:/aaaa/bbbb", "c:/aaaa/bbbb", ""); |
| 790 | | testRelative("c:/aaaa/bbbb", "c:/aaaa/cccc", "..\\cccc"); |
| 791 | | testRelative("c:/aaaa/", "c:/aaaa/cccc", "cccc"); |
| 792 | | testRelative("c:/", "c:\\aaaa\\bbbb", "aaaa\\bbbb"); |
| 793 | | testRelative("c:/aaaa/bbbb", "d:\\", "d:\\"); |
| 794 | | testRelative("c:/AaAa/bbbb", "c:/aaaa/bbbb", ""); |
| 795 | | testRelative("c:/aaaaa/", "c:/aaaa/cccc", "..\\aaaa\\cccc"); |
| 796 | | testRelative("C:\\foo\\bar\\baz\\quux", "C:\\", "..\\..\\..\\.."); |
| 797 | | testRelative("C:\\foo\\test", "C:\\foo\\test\\bar\\package.json", "bar\\package.json"); |
| 798 | | testRelative("C:\\foo\\bar\\baz-quux", "C:\\foo\\bar\\baz", "..\\baz"); |
| 799 | | testRelative("C:\\foo\\bar\\baz", "C:\\foo\\bar\\baz-quux", "..\\baz-quux"); |
| 800 | | testRelative("\\\\foo\\bar", "\\\\foo\\bar\\baz", "baz"); |
| 801 | | testRelative("\\\\foo\\bar\\baz", "\\\\foo\\bar", ".."); |
| 802 | | testRelative("\\\\foo\\bar\\baz-quux", "\\\\foo\\bar\\baz", "..\\baz"); |
| 803 | | testRelative("\\\\foo\\bar\\baz", "\\\\foo\\bar\\baz-quux", "..\\baz-quux"); |
| 804 | | testRelative("C:\\baz-quux", "C:\\baz", "..\\baz"); |
| 805 | | testRelative("C:\\baz", "C:\\baz-quux", "..\\baz-quux"); |
| 806 | | testRelative("\\\\foo\\baz-quux", "\\\\foo\\baz", "..\\baz"); |
| 807 | | testRelative("\\\\foo\\baz", "\\\\foo\\baz-quux", "..\\baz-quux"); |
| 808 | | testRelative("C:\\baz", "\\\\foo\\bar\\baz", "\\\\foo\\bar\\baz"); |
| 809 | | testRelative("\\\\foo\\bar\\baz", "C:\\baz", "C:\\baz") |
| 810 | | } else { |
| 811 | | testRelative("/var/lib", "/var", ".."); |
| 812 | | testRelative("/var/lib", "/bin", "../../bin"); |
| 813 | | testRelative("/var/lib", "/var/lib", ""); |
| 814 | | testRelative("/var/lib", "/var/apache", "../apache"); |
| 815 | | testRelative("/var/", "/var/lib", "lib"); |
| 816 | | testRelative("/", "/var/lib", "var/lib"); |
| 817 | | testRelative("/foo/test", "/foo/test/bar/package.json", "bar/package.json"); |
| 818 | | testRelative("/Users/a/web/b/test/mails", "/Users/a/web/b", "../.."); |
| 819 | | testRelative("/foo/bar/baz-quux", "/foo/bar/baz", "../baz"); |
| 820 | | testRelative("/foo/bar/baz", "/foo/bar/baz-quux", "../baz-quux"); |
| 821 | | testRelative("/baz-quux", "/baz", "../baz"); |
| 822 | | testRelative("/baz", "/baz-quux", "../baz-quux"); |
| 823 | | } |
| 824 | | } |
| 825 | | fn testRelative(from: []const u8, to: []const u8, expected_output: []const u8) { |
| 826 | | const result = %%relative(&debug.global_allocator, from, to); |
| 868 | testRelativeWindows("c:/blah\\blah", "d:/games", "D:\\games"); |
| 869 | testRelativeWindows("c:/aaaa/bbbb", "c:/aaaa", ".."); |
| 870 | testRelativeWindows("c:/aaaa/bbbb", "c:/cccc", "..\\..\\cccc"); |
| 871 | testRelativeWindows("c:/aaaa/bbbb", "c:/aaaa/bbbb", ""); |
| 872 | testRelativeWindows("c:/aaaa/bbbb", "c:/aaaa/cccc", "..\\cccc"); |
| 873 | testRelativeWindows("c:/aaaa/", "c:/aaaa/cccc", "cccc"); |
| 874 | testRelativeWindows("c:/", "c:\\aaaa\\bbbb", "aaaa\\bbbb"); |
| 875 | testRelativeWindows("c:/aaaa/bbbb", "d:\\", "D:\\"); |
| 876 | testRelativeWindows("c:/AaAa/bbbb", "c:/aaaa/bbbb", ""); |
| 877 | testRelativeWindows("c:/aaaaa/", "c:/aaaa/cccc", "..\\aaaa\\cccc"); |
| 878 | testRelativeWindows("C:\\foo\\bar\\baz\\quux", "C:\\", "..\\..\\..\\.."); |
| 879 | testRelativeWindows("C:\\foo\\test", "C:\\foo\\test\\bar\\package.json", "bar\\package.json"); |
| 880 | testRelativeWindows("C:\\foo\\bar\\baz-quux", "C:\\foo\\bar\\baz", "..\\baz"); |
| 881 | testRelativeWindows("C:\\foo\\bar\\baz", "C:\\foo\\bar\\baz-quux", "..\\baz-quux"); |
| 882 | testRelativeWindows("\\\\foo\\bar", "\\\\foo\\bar\\baz", "baz"); |
| 883 | testRelativeWindows("\\\\foo\\bar\\baz", "\\\\foo\\bar", ".."); |
| 884 | testRelativeWindows("\\\\foo\\bar\\baz-quux", "\\\\foo\\bar\\baz", "..\\baz"); |
| 885 | testRelativeWindows("\\\\foo\\bar\\baz", "\\\\foo\\bar\\baz-quux", "..\\baz-quux"); |
| 886 | testRelativeWindows("C:\\baz-quux", "C:\\baz", "..\\baz"); |
| 887 | testRelativeWindows("C:\\baz", "C:\\baz-quux", "..\\baz-quux"); |
| 888 | testRelativeWindows("\\\\foo\\baz-quux", "\\\\foo\\baz", "..\\baz"); |
| 889 | testRelativeWindows("\\\\foo\\baz", "\\\\foo\\baz-quux", "..\\baz-quux"); |
| 890 | testRelativeWindows("C:\\baz", "\\\\foo\\bar\\baz", "\\\\foo\\bar\\baz"); |
| 891 | testRelativeWindows("\\\\foo\\bar\\baz", "C:\\baz", "C:\\baz"); |
| 892 | |
| 893 | testRelativePosix("/var/lib", "/var", ".."); |
| 894 | testRelativePosix("/var/lib", "/bin", "../../bin"); |
| 895 | testRelativePosix("/var/lib", "/var/lib", ""); |
| 896 | testRelativePosix("/var/lib", "/var/apache", "../apache"); |
| 897 | testRelativePosix("/var/", "/var/lib", "lib"); |
| 898 | testRelativePosix("/", "/var/lib", "var/lib"); |
| 899 | testRelativePosix("/foo/test", "/foo/test/bar/package.json", "bar/package.json"); |
| 900 | testRelativePosix("/Users/a/web/b/test/mails", "/Users/a/web/b", "../.."); |
| 901 | testRelativePosix("/foo/bar/baz-quux", "/foo/bar/baz", "../baz"); |
| 902 | testRelativePosix("/foo/bar/baz", "/foo/bar/baz-quux", "../baz-quux"); |
| 903 | testRelativePosix("/baz-quux", "/baz", "../baz"); |
| 904 | testRelativePosix("/baz", "/baz-quux", "../baz-quux"); |
| 905 | } |
| 906 | |
| 907 | fn testRelativePosix(from: []const u8, to: []const u8, expected_output: []const u8) { |
| 908 | const result = %%relativePosix(&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 | 914 | assert(mem.eql(u8, result, expected_output)); |
| 828 | 915 | } |
| 829 | 916 | |