authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2017-10-08 21:16:10-04:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2017-10-08 21:44:24-04:00
log7cfab2fb5f0f0dce81feab9dd8ad3d3e1b435044
treeb55c80b2dc1a82645b2809ab91e9fe35573fb1e4
parente15e1e09f087b8d1f4d80d90a88e2944ead13332

implement std.os.relative for windows


2 files changed, 157 insertions(+), 57 deletions(-)

std/os/index.zig+13
...@@ -1043,3 +1043,16 @@ pub fn posix_setregid(rgid: u32, egid: u32) -> %void {...@@ -1043,3 +1043,16 @@ pub fn posix_setregid(rgid: u32, egid: u32) -> %void {
1043 else => error.Unexpected,1043 else => error.Unexpected,
1044 };1044 };
1045}1045}
1046
1047test "std.os" {
1048 _ = @import("child_process.zig");
1049 _ = @import("darwin_errno.zig");
1050 _ = @import("darwin.zig");
1051 _ = @import("get_user_id.zig");
1052 _ = @import("linux_errno.zig");
1053 //_ = @import("linux_i386.zig");
1054 _ = @import("linux_x86_64.zig");
1055 _ = @import("linux.zig");
1056 _ = @import("path.zig");
1057 _ = @import("windows/index.zig");
1058}
std/os/path.zig+144-57
...@@ -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}
197197
198pub 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);199fn 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}
205208
206fn asciiUpper(byte: u8) -> u8 {209fn 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}
212215
216fn 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`.
214pub fn resolve(allocator: &Allocator, args: ...) -> %[]u8 {228pub 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}
723737
724/// Returns the relative path from ::from to ::to. If ::from and ::to each738/// 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-length739/// 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 `\\`.
727pub fn relative(allocator: &Allocator, from: []const u8, to: []const u8) -> %[]u8 {742pub 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}
734749
735fn windowsRelative(allocator: &Allocator, from: []const u8, to: []const u8) -> %[]u8 {750pub 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}
738821
739fn posixRelative(allocator: &Allocator, from: []const u8, to: []const u8) -> %[]u8 {822pub 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);
742825
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);
745828
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}
783866
784test "os.path.relative" {867test "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}907fn testRelativePosix(from: []const u8, to: []const u8, expected_output: []const u8) {
825fn 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
912fn 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}
829916