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 {
10431043 else => error.Unexpected,
10441044 };
10451045}
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 {
195195 return path[0..1];
196196}
197197
198pub 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.
199fn 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());
204207}
205208
206209fn asciiUpper(byte: u8) -> u8 {
......@@ -210,6 +213,17 @@ fn asciiUpper(byte: u8) -> u8 {
210213 };
211214}
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
213227/// Converts the command line arguments into a slice and calls `resolveSlice`.
214228pub fn resolve(allocator: &Allocator, args: ...) -> %[]u8 {
215229 var paths: [args.len][]const u8 = undefined;
......@@ -721,26 +735,95 @@ fn testBasenameWindows(input: []const u8, expected_output: []const u8) {
721735 assert(mem.eql(u8, basenameWindows(input), expected_output));
722736}
723737
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
726740/// string is returned.
741/// On Windows this canonicalizes the drive to a capital letter and paths to `\\`.
727742pub fn relative(allocator: &Allocator, from: []const u8, to: []const u8) -> %[]u8 {
728743 if (is_windows) {
729 return windowsRelative(allocator, from, to);
744 return relativeWindows(allocator, from, to);
730745 } else {
731 return posixRelative(allocator, from, to);
746 return relativePosix(allocator, from, to);
732747 }
733748}
734749
735fn windowsRelative(allocator: &Allocator, from: []const u8, to: []const u8) -> %[]u8 {
736 @compileError("TODO implement this");
750pub 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{};
737820}
738821
739fn posixRelative(allocator: &Allocator, from: []const u8, to: []const u8) -> %[]u8 {
740 const resolved_from = %return resolve(allocator, from);
822pub fn relativePosix(allocator: &Allocator, from: []const u8, to: []const u8) -> %[]u8 {
823 const resolved_from = %return resolvePosix(allocator, [][]const u8{from});
741824 defer allocator.free(resolved_from);
742825
743 const resolved_to = %return resolve(allocator, to);
826 const resolved_to = %return resolvePosix(allocator, [][]const u8{to});
744827 defer allocator.free(resolved_to);
745828
746829 var from_it = mem.split(resolved_from, "/");
......@@ -782,48 +865,52 @@ fn posixRelative(allocator: &Allocator, from: []const u8, to: []const u8) -> %[]
782865}
783866
784867test "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}
825fn 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
907fn 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
912fn testRelativeWindows(from: []const u8, to: []const u8, expected_output: []const u8) {
913 const result = %%relativeWindows(&debug.global_allocator, from, to);
827914 assert(mem.eql(u8, result, expected_output));
828915}
829916