authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2017-10-06 17:21:21-04:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2017-10-08 21:43:58-04:00
logdcf5c9074e8a0feab204200d74831fda10e6c625
treec3aea70f6791c3ebcc0d52be0c6fc2d892d46c49
parent08ee69dac360dda17e22d950246e42228fc54f47

more std.os.path work for windows


4 files changed, 111 insertions(+), 75 deletions(-)

std/build.zig+2-2
...@@ -309,7 +309,7 @@ pub const Builder = struct {...@@ -309,7 +309,7 @@ pub const Builder = struct {
309309
310 fn processNixOSEnvVars(self: &Builder) {310 fn processNixOSEnvVars(self: &Builder) {
311 if (os.getEnv("NIX_CFLAGS_COMPILE")) |nix_cflags_compile| {311 if (os.getEnv("NIX_CFLAGS_COMPILE")) |nix_cflags_compile| {
312 var it = mem.split(nix_cflags_compile, ' ');312 var it = mem.split(nix_cflags_compile, " ");
313 while (true) {313 while (true) {
314 const word = it.next() ?? break;314 const word = it.next() ?? break;
315 if (mem.eql(u8, word, "-isystem")) {315 if (mem.eql(u8, word, "-isystem")) {
...@@ -325,7 +325,7 @@ pub const Builder = struct {...@@ -325,7 +325,7 @@ pub const Builder = struct {
325 }325 }
326 }326 }
327 if (os.getEnv("NIX_LDFLAGS")) |nix_ldflags| {327 if (os.getEnv("NIX_LDFLAGS")) |nix_ldflags| {
328 var it = mem.split(nix_ldflags, ' ');328 var it = mem.split(nix_ldflags, " ");
329 while (true) {329 while (true) {
330 const word = it.next() ?? break;330 const word = it.next() ?? break;
331 if (mem.eql(u8, word, "-rpath")) {331 if (mem.eql(u8, word, "-rpath")) {
std/mem.zig+10-2
...@@ -324,7 +324,7 @@ pub fn split(buffer: []const u8, split_bytes: []const u8) -> SplitIterator {...@@ -324,7 +324,7 @@ pub fn split(buffer: []const u8, split_bytes: []const u8) -> SplitIterator {
324}324}
325325
326test "mem.split" {326test "mem.split" {
327 var it = split(" abc def ghi ", ' ');327 var it = split(" abc def ghi ", " ");
328 assert(eql(u8, ??it.next(), "abc"));328 assert(eql(u8, ??it.next(), "abc"));
329 assert(eql(u8, ??it.next(), "def"));329 assert(eql(u8, ??it.next(), "def"));
330 assert(eql(u8, ??it.next(), "ghi"));330 assert(eql(u8, ??it.next(), "ghi"));
...@@ -355,7 +355,15 @@ const SplitIterator = struct {...@@ -355,7 +355,15 @@ const SplitIterator = struct {
355 return self.buffer[start..end];355 return self.buffer[start..end];
356 }356 }
357357
358 fn isSplitByte(self: &SplitIterator, byte: u8) -> bool {358 /// Returns a slice of the remaining bytes. Does not affect iterator state.
359 pub fn rest(self: &const SplitIterator) -> []const u8 {
360 // move to beginning of token
361 var index: usize = self.index;
362 while (index < self.buffer.len and self.isSplitByte(self.buffer[index])) : (index += 1) {}
363 return self.buffer[index..];
364 }
365
366 fn isSplitByte(self: &const SplitIterator, byte: u8) -> bool {
359 for (self.split_bytes) |split_byte| {367 for (self.split_bytes) |split_byte| {
360 if (byte == split_byte) {368 if (byte == split_byte) {
361 return true;369 return true;
std/os/index.zig+1-1
...@@ -384,7 +384,7 @@ pub fn posixExecve(argv: []const []const u8, env_map: &const BufMap,...@@ -384,7 +384,7 @@ pub fn posixExecve(argv: []const []const u8, env_map: &const BufMap,
384 // +1 for the null terminating byte384 // +1 for the null terminating byte
385 const path_buf = %return allocator.alloc(u8, PATH.len + exe_path.len + 2);385 const path_buf = %return allocator.alloc(u8, PATH.len + exe_path.len + 2);
386 defer allocator.free(path_buf);386 defer allocator.free(path_buf);
387 var it = mem.split(PATH, ':');387 var it = mem.split(PATH, ":");
388 var seen_eacces = false;388 var seen_eacces = false;
389 var err: usize = undefined;389 var err: usize = undefined;
390 while (it.next()) |search_path| {390 while (it.next()) |search_path| {
std/os/path.zig+98-70
...@@ -139,18 +139,18 @@ pub fn networkShare(path: []const u8) -> ?[]const u8 {...@@ -139,18 +139,18 @@ pub fn networkShare(path: []const u8) -> ?[]const u8 {
139 if (path.len < "//a/b".len)139 if (path.len < "//a/b".len)
140 return null;140 return null;
141141
142 // TODO when I combined these together with `inline for` the compiler crashed
142 {143 {
143 const this_sep = '/';144 const this_sep = '/';
144 const two_sep = []u8{this_sep, this_sep};145 const two_sep = []u8{this_sep, this_sep};
145 if (mem.startsWith(u8, path, two_sep)) {146 if (mem.startsWith(u8, path, two_sep)) {
146 if (path[2] == this_sep)147 if (path[2] == this_sep)
147 return null;148 return null;
148 const index_host = mem.indexOfScalarPos(u8, path, 3, this_sep) ?? return null;149
149 const next_start = index_host + 1;150 var it = mem.split(path, []u8{this_sep});
150 if (next_start >= path.len)151 _ = (it.next() ?? return null);
151 return null;152 _ = (it.next() ?? return null);
152 const index_root = mem.indexOfScalarPos(u8, path, next_start, this_sep) ?? path.len;153 return path[0..it.index];
153 return path[0..index_root];
154 }154 }
155 }155 }
156 {156 {
...@@ -159,12 +159,11 @@ pub fn networkShare(path: []const u8) -> ?[]const u8 {...@@ -159,12 +159,11 @@ pub fn networkShare(path: []const u8) -> ?[]const u8 {
159 if (mem.startsWith(u8, path, two_sep)) {159 if (mem.startsWith(u8, path, two_sep)) {
160 if (path[2] == this_sep)160 if (path[2] == this_sep)
161 return null;161 return null;
162 const index_host = mem.indexOfScalarPos(u8, path, 3, this_sep) ?? return null;162
163 const next_start = index_host + 1;163 var it = mem.split(path, []u8{this_sep});
164 if (next_start >= path.len)164 _ = (it.next() ?? return null);
165 return null;165 _ = (it.next() ?? return null);
166 const index_root = mem.indexOfScalarPos(u8, path, next_start, this_sep) ?? path.len;166 return path[0..it.index];
167 return path[0..index_root];
168 }167 }
169 }168 }
170 return null;169 return null;
...@@ -177,23 +176,6 @@ test "os.path.networkShare" {...@@ -177,23 +176,6 @@ test "os.path.networkShare" {
177 assert(networkShare("\\\\a\\") == null);176 assert(networkShare("\\\\a\\") == null);
178}177}
179178
180pub fn preferredSepWindows(path: []const u8) -> u8 {
181 for (path) |byte| {
182 if (byte == '/' or byte == '\\') {
183 return byte;
184 }
185 }
186 return sep_windows;
187}
188
189pub fn preferredSep(path: []const u8) -> u8 {
190 if (is_windows) {
191 return preferredSepWindows(path);
192 } else {
193 return sep_posix;
194 }
195}
196
197pub fn root(path: []const u8) -> []const u8 {179pub fn root(path: []const u8) -> []const u8 {
198 if (is_windows) {180 if (is_windows) {
199 return rootWindows(path);181 return rootWindows(path);
...@@ -206,7 +188,7 @@ pub fn rootWindows(path: []const u8) -> []const u8 {...@@ -206,7 +188,7 @@ pub fn rootWindows(path: []const u8) -> []const u8 {
206 return drive(path) ?? (networkShare(path) ?? []u8{});188 return drive(path) ?? (networkShare(path) ?? []u8{});
207}189}
208190
209pub fn rootPosix(path: []const u8) -> ?[]const u8 {191pub fn rootPosix(path: []const u8) -> []const u8 {
210 if (path.len == 0 or path[0] != '/')192 if (path.len == 0 or path[0] != '/')
211 return []u8{};193 return []u8{};
212194
...@@ -218,18 +200,17 @@ pub fn drivesEqual(drive1: []const u8, drive2: []const u8) -> bool {...@@ -218,18 +200,17 @@ pub fn drivesEqual(drive1: []const u8, drive2: []const u8) -> bool {
218 assert(drive2.len == 2);200 assert(drive2.len == 2);
219 assert(drive1[1] == ':');201 assert(drive1[1] == ':');
220 assert(drive2[1] == ':');202 assert(drive2[1] == ':');
221 return asciiLower(drive1[0]) == asciiLower(drive2[0]);203 return asciiUpper(drive1[0]) == asciiUpper(drive2[0]);
222}204}
223205
224fn asciiLower(byte: u8) -> u8 {206fn asciiUpper(byte: u8) -> u8 {
225 return switch (byte) {207 return switch (byte) {
226 'A' ... 'Z' => 'a' + (byte - 'A'),208 'a' ... 'z' => 'A' + (byte - 'a'),
227 else => byte,209 else => byte,
228 };210 };
229}211}
230212
231/// This function is like a series of `cd` statements executed one after another.213/// Converts the command line arguments into a slice and calls `resolveSlice`.
232/// The result does not have a trailing path separator.
233pub fn resolve(allocator: &Allocator, args: ...) -> %[]u8 {214pub fn resolve(allocator: &Allocator, args: ...) -> %[]u8 {
234 var paths: [args.len][]const u8 = undefined;215 var paths: [args.len][]const u8 = undefined;
235 comptime var arg_i = 0;216 comptime var arg_i = 0;
...@@ -239,6 +220,7 @@ pub fn resolve(allocator: &Allocator, args: ...) -> %[]u8 {...@@ -239,6 +220,7 @@ pub fn resolve(allocator: &Allocator, args: ...) -> %[]u8 {
239 return resolveSlice(allocator, paths);220 return resolveSlice(allocator, paths);
240}221}
241222
223/// On Windows, this calls `resolveWindows` and on POSIX it calls `resolvePosix`.
242pub fn resolveSlice(allocator: &Allocator, paths: []const []const u8) -> %[]u8 {224pub fn resolveSlice(allocator: &Allocator, paths: []const []const u8) -> %[]u8 {
243 if (is_windows) {225 if (is_windows) {
244 return resolveWindows(allocator, paths);226 return resolveWindows(allocator, paths);
...@@ -247,6 +229,12 @@ pub fn resolveSlice(allocator: &Allocator, paths: []const []const u8) -> %[]u8 {...@@ -247,6 +229,12 @@ pub fn resolveSlice(allocator: &Allocator, paths: []const []const u8) -> %[]u8 {
247 }229 }
248}230}
249231
232/// This function is like a series of `cd` statements executed one after another.
233/// It resolves "." and "..".
234/// The result does not have a trailing path separator.
235/// If all paths are relative it uses the current working directory as a starting point.
236/// Each drive has its own current working directory.
237/// Path separators are canonicalized to '\\' and drives are canonicalized to capital letters.
250pub fn resolveWindows(allocator: &Allocator, paths: []const []const u8) -> %[]u8 {238pub fn resolveWindows(allocator: &Allocator, paths: []const []const u8) -> %[]u8 {
251 if (paths.len == 0) {239 if (paths.len == 0) {
252 assert(is_windows); // resolveWindows called on non windows can't use getCwd240 assert(is_windows); // resolveWindows called on non windows can't use getCwd
...@@ -254,7 +242,7 @@ pub fn resolveWindows(allocator: &Allocator, paths: []const []const u8) -> %[]u8...@@ -254,7 +242,7 @@ pub fn resolveWindows(allocator: &Allocator, paths: []const []const u8) -> %[]u8
254 }242 }
255243
256 // determine which drive we want to result with244 // determine which drive we want to result with
257 var result_drive: ?[]const u8 = null;245 var result_drive_upcase: ?u8 = null;
258 var have_abs = false;246 var have_abs = false;
259 var first_index: usize = 0;247 var first_index: usize = 0;
260 var max_size: usize = 0;248 var max_size: usize = 0;
...@@ -266,25 +254,28 @@ pub fn resolveWindows(allocator: &Allocator, paths: []const []const u8) -> %[]u8...@@ -266,25 +254,28 @@ pub fn resolveWindows(allocator: &Allocator, paths: []const []const u8) -> %[]u8
266 max_size = 0;254 max_size = 0;
267 }255 }
268 if (drive(p)) |d| {256 if (drive(p)) |d| {
269 result_drive = d;257 result_drive_upcase = asciiUpper(d[0]);
270 } else if (is_abs) {258 } else if (networkShare(p)) |_| {
271 result_drive = null;259 result_drive_upcase = null;
272 }260 }
273 max_size += p.len + 1;261 max_size += p.len + 1;
274 }262 }
275263
264
276 // if we will result with a drive, loop again to determine265 // if we will result with a drive, loop again to determine
277 // which is the first time the drive is absolutely specified, if any266 // which is the first time the drive is absolutely specified, if any
278 // and count up the max bytes for paths related to this drive267 // and count up the max bytes for paths related to this drive
279 if (result_drive) |res_dr| {268 if (result_drive_upcase) |res_dr| {
280 have_abs = false;269 have_abs = false;
281 first_index = 0;270 first_index = 0;
282 max_size = 0;271 max_size = "_:".len;
283 var correct_drive = false;272 var correct_drive = false;
284273
285 for (paths) |p, i| {274 for (paths) |p, i| {
286 if (drive(p)) |dr| {275 if (drive(p)) |dr| {
287 correct_drive = drivesEqual(dr, res_dr);276 correct_drive = asciiUpper(dr[0]) == res_dr;
277 } else if (networkShare(p)) |_| {
278 continue;
288 }279 }
289 if (!correct_drive) {280 if (!correct_drive) {
290 continue;281 continue;
...@@ -292,20 +283,46 @@ pub fn resolveWindows(allocator: &Allocator, paths: []const []const u8) -> %[]u8...@@ -292,20 +283,46 @@ pub fn resolveWindows(allocator: &Allocator, paths: []const []const u8) -> %[]u8
292 const is_abs = isAbsoluteWindows(p);283 const is_abs = isAbsoluteWindows(p);
293 if (is_abs) {284 if (is_abs) {
294 first_index = i;285 first_index = i;
295 max_size = 0;286 max_size = "_:".len;
287 have_abs = true;
296 }288 }
297 max_size += p.len + 1;289 max_size += p.len + 1;
298 }290 }
299 }291 }
300292
293 var drive_buf = "_:";
301 var result: []u8 = undefined;294 var result: []u8 = undefined;
302 var result_index: usize = 0;295 var result_index: usize = 0;
296 var root_slice: []const u8 = undefined;
303297
304 if (have_abs) {298 if (have_abs) {
305 result = %return allocator.alloc(u8, max_size);299 result = %return allocator.alloc(u8, max_size);
306 mem.copy(u8, result, paths[first_index]);300
307 result_index += paths[first_index].len;301 if (result_drive_upcase) |res_dr| {
308 first_index += 1;302 drive_buf[0] = res_dr;
303 root_slice = drive_buf[0..];
304
305 mem.copy(u8, result, root_slice);
306 result_index += root_slice.len;
307 } else {
308 // We know it looks like //a/b or \\a\b because of earlier code
309 var it = mem.split(paths[first_index], "/\\");
310 const server_name = ??it.next();
311 const other_name = ??it.next();
312
313 result[result_index] = '\\';
314 result_index += 1;
315 result[result_index] = '\\';
316 result_index += 1;
317 mem.copy(u8, result[result_index..], server_name);
318 result_index += server_name.len;
319 result[result_index] = '\\';
320 result_index += 1;
321 mem.copy(u8, result[result_index..], other_name);
322 result_index += other_name.len;
323
324 root_slice = result[0..result_index];
325 }
309 } else {326 } else {
310 assert(is_windows); // resolveWindows called on non windows can't use getCwd327 assert(is_windows); // resolveWindows called on non windows can't use getCwd
311 // TODO get cwd for result_drive if applicable328 // TODO get cwd for result_drive if applicable
...@@ -314,35 +331,37 @@ pub fn resolveWindows(allocator: &Allocator, paths: []const []const u8) -> %[]u8...@@ -314,35 +331,37 @@ pub fn resolveWindows(allocator: &Allocator, paths: []const []const u8) -> %[]u8
314 result = %return allocator.alloc(u8, max_size + cwd.len + 1);331 result = %return allocator.alloc(u8, max_size + cwd.len + 1);
315 mem.copy(u8, result, cwd);332 mem.copy(u8, result, cwd);
316 result_index += cwd.len;333 result_index += cwd.len;
334
335 root_slice = rootWindows(result[0..result_index]);
317 }336 }
318 %defer allocator.free(result);337 %defer allocator.free(result);
319338
320 var correct_drive = false;339 var correct_drive = true;
321 const rootSlice = rootWindows(result[0..result_index]);
322 const preferred_path_sep = preferredSepWindows(result[0..result_index]);
323 for (paths[first_index..]) |p, i| {340 for (paths[first_index..]) |p, i| {
324 if (result_drive) |res_dr| {341 if (result_drive_upcase) |res_dr| {
325 if (drive(p)) |dr| {342 if (drive(p)) |dr| {
326 correct_drive = drivesEqual(dr, res_dr);343 correct_drive = asciiUpper(dr[0]) == res_dr;
344 } else if (networkShare(p)) |_| {
345 continue;
327 }346 }
328 if (!correct_drive) {347 if (!correct_drive) {
329 continue;348 continue;
330 }349 }
331 }350 }
332 var it = mem.split(p, "/\\");351 var it = mem.split(p[rootWindows(p).len..], "/\\");
333 while (it.next()) |component| {352 while (it.next()) |component| {
334 if (mem.eql(u8, component, ".")) {353 if (mem.eql(u8, component, ".")) {
335 continue;354 continue;
336 } else if (mem.eql(u8, component, "..")) {355 } else if (mem.eql(u8, component, "..")) {
337 while (true) {356 while (true) {
338 if (result_index == 0 or result_index == rootSlice.len)357 if (result_index == 0 or result_index == root_slice.len)
339 break;358 break;
340 result_index -= 1;359 result_index -= 1;
341 if (result[result_index] == '\\' or result[result_index] == '/')360 if (result[result_index] == '\\' or result[result_index] == '/')
342 break;361 break;
343 }362 }
344 } else {363 } else {
345 result[result_index] = preferred_path_sep;364 result[result_index] = sep_windows;
346 result_index += 1;365 result_index += 1;
347 mem.copy(u8, result[result_index..], component);366 mem.copy(u8, result[result_index..], component);
348 result_index += component.len;367 result_index += component.len;
...@@ -350,9 +369,18 @@ pub fn resolveWindows(allocator: &Allocator, paths: []const []const u8) -> %[]u8...@@ -350,9 +369,18 @@ pub fn resolveWindows(allocator: &Allocator, paths: []const []const u8) -> %[]u8
350 }369 }
351 }370 }
352371
372 if (result_index == root_slice.len) {
373 result[result_index] = '\\';
374 result_index += 1;
375 }
376
353 return result[0..result_index];377 return result[0..result_index];
354}378}
355379
380/// This function is like a series of `cd` statements executed one after another.
381/// It resolves "." and "..".
382/// The result does not have a trailing path separator.
383/// If all paths are relative it uses the current working directory as a starting point.
356pub fn resolvePosix(allocator: &Allocator, paths: []const []const u8) -> %[]u8 {384pub fn resolvePosix(allocator: &Allocator, paths: []const []const u8) -> %[]u8 {
357 if (paths.len == 0) {385 if (paths.len == 0) {
358 assert(!is_windows); // resolvePosix called on windows can't use getCwd386 assert(!is_windows); // resolvePosix called on windows can't use getCwd
...@@ -427,17 +455,17 @@ test "os.path.resolve" {...@@ -427,17 +455,17 @@ test "os.path.resolve" {
427}455}
428456
429test "os.path.resolveWindows" {457test "os.path.resolveWindows" {
430 assert(mem.eql(u8, testResolveWindows([][]const u8{"c:/blah\\blah", "d:/games", "c:../a"}), "c:\\blah\\a"));458 assert(mem.eql(u8, testResolveWindows([][]const u8{"c:/blah\\blah", "d:/games", "c:../a"}), "C:\\blah\\a"));
431 assert(mem.eql(u8, testResolveWindows([][]const u8{"c:/blah\\blah", "d:/games", "C:../a"}), "c:\\blah\\a"));459 assert(mem.eql(u8, testResolveWindows([][]const u8{"c:/blah\\blah", "d:/games", "C:../a"}), "C:\\blah\\a"));
432 assert(mem.eql(u8, testResolveWindows([][]const u8{"c:/ignore", "d:\\a/b\\c/d", "\\e.exe"}), "d:\\e.exe"));460 assert(mem.eql(u8, testResolveWindows([][]const u8{"c:/ignore", "d:\\a/b\\c/d", "\\e.exe"}), "D:\\e.exe"));
433 assert(mem.eql(u8, testResolveWindows([][]const u8{"c:/ignore", "c:/some/file"}), "c:\\some\\file"));461 assert(mem.eql(u8, testResolveWindows([][]const u8{"c:/ignore", "c:/some/file"}), "C:\\some\\file"));
434 assert(mem.eql(u8, testResolveWindows([][]const u8{"d:/ignore", "d:some/dir//"}), "d:\\ignore\\some\\dir"));462 assert(mem.eql(u8, testResolveWindows([][]const u8{"d:/ignore", "d:some/dir//"}), "D:\\ignore\\some\\dir"));
435 assert(mem.eql(u8, testResolveWindows([][]const u8{"//server/share", "..", "relative\\"}), "\\\\server\\share\\relative"));463 assert(mem.eql(u8, testResolveWindows([][]const u8{"//server/share", "..", "relative\\"}), "\\\\server\\share\\relative"));
436 assert(mem.eql(u8, testResolveWindows([][]const u8{"c:/", "//"}), "c:\\"));464 assert(mem.eql(u8, testResolveWindows([][]const u8{"c:/", "//"}), "C:\\"));
437 assert(mem.eql(u8, testResolveWindows([][]const u8{"c:/", "//dir"}), "c:\\dir"));465 assert(mem.eql(u8, testResolveWindows([][]const u8{"c:/", "//dir"}), "C:\\dir"));
438 assert(mem.eql(u8, testResolveWindows([][]const u8{"c:/", "//server/share"}), "\\\\server\\share\\"));466 assert(mem.eql(u8, testResolveWindows([][]const u8{"c:/", "//server/share"}), "\\\\server\\share\\"));
439 assert(mem.eql(u8, testResolveWindows([][]const u8{"c:/", "//server//share"}), "\\\\server\\share\\"));467 assert(mem.eql(u8, testResolveWindows([][]const u8{"c:/", "//server//share"}), "\\\\server\\share\\"));
440 assert(mem.eql(u8, testResolveWindows([][]const u8{"c:/", "///some//dir"}), "c:\\some\\dir"));468 assert(mem.eql(u8, testResolveWindows([][]const u8{"c:/", "///some//dir"}), "C:\\some\\dir"));
441 assert(mem.eql(u8, testResolveWindows([][]const u8{"C:\\foo\\tmp.3\\", "..\\tmp.3\\cycles\\root.js"}),469 assert(mem.eql(u8, testResolveWindows([][]const u8{"C:\\foo\\tmp.3\\", "..\\tmp.3\\cycles\\root.js"}),
442 "C:\\foo\\tmp.3\\cycles\\root.js"));470 "C:\\foo\\tmp.3\\cycles\\root.js"));
443}471}
...@@ -475,27 +503,27 @@ pub fn dirnameWindows(path: []const u8) -> []const u8 {...@@ -475,27 +503,27 @@ pub fn dirnameWindows(path: []const u8) -> []const u8 {
475 if (path.len == 0)503 if (path.len == 0)
476 return path[0..0];504 return path[0..0];
477505
478 const rootSlice = rootWindows(path);506 const root_slice = rootWindows(path);
479 if (path.len == rootSlice.len)507 if (path.len == root_slice.len)
480 return path;508 return path;
481509
482 const have_root_slash = path.len > rootSlice.len and (path[rootSlice.len] == '/' or path[rootSlice.len] == '\\');510 const have_root_slash = path.len > root_slice.len and (path[root_slice.len] == '/' or path[root_slice.len] == '\\');
483511
484 var end_index: usize = path.len - 1;512 var end_index: usize = path.len - 1;
485513
486 while ((path[end_index] == '/' or path[end_index] == '\\') and end_index > rootSlice.len) {514 while ((path[end_index] == '/' or path[end_index] == '\\') and end_index > root_slice.len) {
487 if (end_index == 0)515 if (end_index == 0)
488 return path[0..0];516 return path[0..0];
489 end_index -= 1;517 end_index -= 1;
490 }518 }
491519
492 while (path[end_index] != '/' and path[end_index] != '\\' and end_index > rootSlice.len) {520 while (path[end_index] != '/' and path[end_index] != '\\' and end_index > root_slice.len) {
493 if (end_index == 0)521 if (end_index == 0)
494 return path[0..0];522 return path[0..0];
495 end_index -= 1;523 end_index -= 1;
496 }524 }
497525
498 if (have_root_slash and end_index == rootSlice.len) {526 if (have_root_slash and end_index == root_slice.len) {
499 end_index += 1;527 end_index += 1;
500 }528 }
501529
...@@ -645,8 +673,8 @@ fn posixRelative(allocator: &Allocator, from: []const u8, to: []const u8) -> %[]...@@ -645,8 +673,8 @@ fn posixRelative(allocator: &Allocator, from: []const u8, to: []const u8) -> %[]
645 const resolved_to = %return resolve(allocator, to);673 const resolved_to = %return resolve(allocator, to);
646 defer allocator.free(resolved_to);674 defer allocator.free(resolved_to);
647675
648 var from_it = mem.split(resolved_from, '/');676 var from_it = mem.split(resolved_from, "/");
649 var to_it = mem.split(resolved_to, '/');677 var to_it = mem.split(resolved_to, "/");
650 while (true) {678 while (true) {
651 const from_component = from_it.next() ?? return mem.dupe(allocator, u8, to_it.rest());679 const from_component = from_it.next() ?? return mem.dupe(allocator, u8, to_it.rest());
652 const to_rest = to_it.rest();680 const to_rest = to_it.rest();