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 {
309309
310310 fn processNixOSEnvVars(self: &Builder) {
311311 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, " ");
313313 while (true) {
314314 const word = it.next() ?? break;
315315 if (mem.eql(u8, word, "-isystem")) {
......@@ -325,7 +325,7 @@ pub const Builder = struct {
325325 }
326326 }
327327 if (os.getEnv("NIX_LDFLAGS")) |nix_ldflags| {
328 var it = mem.split(nix_ldflags, ' ');
328 var it = mem.split(nix_ldflags, " ");
329329 while (true) {
330330 const word = it.next() ?? break;
331331 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 {
324324}
325325
326326test "mem.split" {
327 var it = split(" abc def ghi ", ' ');
327 var it = split(" abc def ghi ", " ");
328328 assert(eql(u8, ??it.next(), "abc"));
329329 assert(eql(u8, ??it.next(), "def"));
330330 assert(eql(u8, ??it.next(), "ghi"));
......@@ -355,7 +355,15 @@ const SplitIterator = struct {
355355 return self.buffer[start..end];
356356 }
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 {
359367 for (self.split_bytes) |split_byte| {
360368 if (byte == split_byte) {
361369 return true;
std/os/index.zig+1-1
......@@ -384,7 +384,7 @@ pub fn posixExecve(argv: []const []const u8, env_map: &const BufMap,
384384 // +1 for the null terminating byte
385385 const path_buf = %return allocator.alloc(u8, PATH.len + exe_path.len + 2);
386386 defer allocator.free(path_buf);
387 var it = mem.split(PATH, ':');
387 var it = mem.split(PATH, ":");
388388 var seen_eacces = false;
389389 var err: usize = undefined;
390390 while (it.next()) |search_path| {
std/os/path.zig+98-70
......@@ -139,18 +139,18 @@ pub fn networkShare(path: []const u8) -> ?[]const u8 {
139139 if (path.len < "//a/b".len)
140140 return null;
141141
142 // TODO when I combined these together with `inline for` the compiler crashed
142143 {
143144 const this_sep = '/';
144145 const two_sep = []u8{this_sep, this_sep};
145146 if (mem.startsWith(u8, path, two_sep)) {
146147 if (path[2] == this_sep)
147148 return null;
148 const index_host = mem.indexOfScalarPos(u8, path, 3, this_sep) ?? return null;
149 const next_start = index_host + 1;
150 if (next_start >= path.len)
151 return null;
152 const index_root = mem.indexOfScalarPos(u8, path, next_start, this_sep) ?? path.len;
153 return path[0..index_root];
149
150 var it = mem.split(path, []u8{this_sep});
151 _ = (it.next() ?? return null);
152 _ = (it.next() ?? return null);
153 return path[0..it.index];
154154 }
155155 }
156156 {
......@@ -159,12 +159,11 @@ pub fn networkShare(path: []const u8) -> ?[]const u8 {
159159 if (mem.startsWith(u8, path, two_sep)) {
160160 if (path[2] == this_sep)
161161 return null;
162 const index_host = mem.indexOfScalarPos(u8, path, 3, this_sep) ?? return null;
163 const next_start = index_host + 1;
164 if (next_start >= path.len)
165 return null;
166 const index_root = mem.indexOfScalarPos(u8, path, next_start, this_sep) ?? path.len;
167 return path[0..index_root];
162
163 var it = mem.split(path, []u8{this_sep});
164 _ = (it.next() ?? return null);
165 _ = (it.next() ?? return null);
166 return path[0..it.index];
168167 }
169168 }
170169 return null;
......@@ -177,23 +176,6 @@ test "os.path.networkShare" {
177176 assert(networkShare("\\\\a\\") == null);
178177}
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
197179pub fn root(path: []const u8) -> []const u8 {
198180 if (is_windows) {
199181 return rootWindows(path);
......@@ -206,7 +188,7 @@ pub fn rootWindows(path: []const u8) -> []const u8 {
206188 return drive(path) ?? (networkShare(path) ?? []u8{});
207189}
208190
209pub fn rootPosix(path: []const u8) -> ?[]const u8 {
191pub fn rootPosix(path: []const u8) -> []const u8 {
210192 if (path.len == 0 or path[0] != '/')
211193 return []u8{};
212194
......@@ -218,18 +200,17 @@ pub fn drivesEqual(drive1: []const u8, drive2: []const u8) -> bool {
218200 assert(drive2.len == 2);
219201 assert(drive1[1] == ':');
220202 assert(drive2[1] == ':');
221 return asciiLower(drive1[0]) == asciiLower(drive2[0]);
203 return asciiUpper(drive1[0]) == asciiUpper(drive2[0]);
222204}
223205
224fn asciiLower(byte: u8) -> u8 {
206fn asciiUpper(byte: u8) -> u8 {
225207 return switch (byte) {
226 'A' ... 'Z' => 'a' + (byte - 'A'),
208 'a' ... 'z' => 'A' + (byte - 'a'),
227209 else => byte,
228210 };
229211}
230212
231/// This function is like a series of `cd` statements executed one after another.
232/// The result does not have a trailing path separator.
213/// Converts the command line arguments into a slice and calls `resolveSlice`.
233214pub fn resolve(allocator: &Allocator, args: ...) -> %[]u8 {
234215 var paths: [args.len][]const u8 = undefined;
235216 comptime var arg_i = 0;
......@@ -239,6 +220,7 @@ pub fn resolve(allocator: &Allocator, args: ...) -> %[]u8 {
239220 return resolveSlice(allocator, paths);
240221}
241222
223/// On Windows, this calls `resolveWindows` and on POSIX it calls `resolvePosix`.
242224pub fn resolveSlice(allocator: &Allocator, paths: []const []const u8) -> %[]u8 {
243225 if (is_windows) {
244226 return resolveWindows(allocator, paths);
......@@ -247,6 +229,12 @@ pub fn resolveSlice(allocator: &Allocator, paths: []const []const u8) -> %[]u8 {
247229 }
248230}
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.
250238pub fn resolveWindows(allocator: &Allocator, paths: []const []const u8) -> %[]u8 {
251239 if (paths.len == 0) {
252240 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
254242 }
255243
256244 // determine which drive we want to result with
257 var result_drive: ?[]const u8 = null;
245 var result_drive_upcase: ?u8 = null;
258246 var have_abs = false;
259247 var first_index: usize = 0;
260248 var max_size: usize = 0;
......@@ -266,25 +254,28 @@ pub fn resolveWindows(allocator: &Allocator, paths: []const []const u8) -> %[]u8
266254 max_size = 0;
267255 }
268256 if (drive(p)) |d| {
269 result_drive = d;
270 } else if (is_abs) {
271 result_drive = null;
257 result_drive_upcase = asciiUpper(d[0]);
258 } else if (networkShare(p)) |_| {
259 result_drive_upcase = null;
272260 }
273261 max_size += p.len + 1;
274262 }
275263
264
276265 // if we will result with a drive, loop again to determine
277266 // which is the first time the drive is absolutely specified, if any
278267 // 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| {
280269 have_abs = false;
281270 first_index = 0;
282 max_size = 0;
271 max_size = "_:".len;
283272 var correct_drive = false;
284273
285274 for (paths) |p, i| {
286275 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;
288279 }
289280 if (!correct_drive) {
290281 continue;
......@@ -292,20 +283,46 @@ pub fn resolveWindows(allocator: &Allocator, paths: []const []const u8) -> %[]u8
292283 const is_abs = isAbsoluteWindows(p);
293284 if (is_abs) {
294285 first_index = i;
295 max_size = 0;
286 max_size = "_:".len;
287 have_abs = true;
296288 }
297289 max_size += p.len + 1;
298290 }
299291 }
300292
293 var drive_buf = "_:";
301294 var result: []u8 = undefined;
302295 var result_index: usize = 0;
296 var root_slice: []const u8 = undefined;
303297
304298 if (have_abs) {
305299 result = %return allocator.alloc(u8, max_size);
306 mem.copy(u8, result, paths[first_index]);
307 result_index += paths[first_index].len;
308 first_index += 1;
300
301 if (result_drive_upcase) |res_dr| {
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 }
309326 } else {
310327 assert(is_windows); // resolveWindows called on non windows can't use getCwd
311328 // TODO get cwd for result_drive if applicable
......@@ -314,35 +331,37 @@ pub fn resolveWindows(allocator: &Allocator, paths: []const []const u8) -> %[]u8
314331 result = %return allocator.alloc(u8, max_size + cwd.len + 1);
315332 mem.copy(u8, result, cwd);
316333 result_index += cwd.len;
334
335 root_slice = rootWindows(result[0..result_index]);
317336 }
318337 %defer allocator.free(result);
319338
320 var correct_drive = false;
321 const rootSlice = rootWindows(result[0..result_index]);
322 const preferred_path_sep = preferredSepWindows(result[0..result_index]);
339 var correct_drive = true;
323340 for (paths[first_index..]) |p, i| {
324 if (result_drive) |res_dr| {
341 if (result_drive_upcase) |res_dr| {
325342 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;
327346 }
328347 if (!correct_drive) {
329348 continue;
330349 }
331350 }
332 var it = mem.split(p, "/\\");
351 var it = mem.split(p[rootWindows(p).len..], "/\\");
333352 while (it.next()) |component| {
334353 if (mem.eql(u8, component, ".")) {
335354 continue;
336355 } else if (mem.eql(u8, component, "..")) {
337356 while (true) {
338 if (result_index == 0 or result_index == rootSlice.len)
357 if (result_index == 0 or result_index == root_slice.len)
339358 break;
340359 result_index -= 1;
341360 if (result[result_index] == '\\' or result[result_index] == '/')
342361 break;
343362 }
344363 } else {
345 result[result_index] = preferred_path_sep;
364 result[result_index] = sep_windows;
346365 result_index += 1;
347366 mem.copy(u8, result[result_index..], component);
348367 result_index += component.len;
......@@ -350,9 +369,18 @@ pub fn resolveWindows(allocator: &Allocator, paths: []const []const u8) -> %[]u8
350369 }
351370 }
352371
372 if (result_index == root_slice.len) {
373 result[result_index] = '\\';
374 result_index += 1;
375 }
376
353377 return result[0..result_index];
354378}
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.
356384pub fn resolvePosix(allocator: &Allocator, paths: []const []const u8) -> %[]u8 {
357385 if (paths.len == 0) {
358386 assert(!is_windows); // resolvePosix called on windows can't use getCwd
......@@ -427,17 +455,17 @@ test "os.path.resolve" {
427455}
428456
429457test "os.path.resolveWindows" {
430 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"));
432 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"));
434 assert(mem.eql(u8, testResolveWindows([][]const u8{"d:/ignore", "d:some/dir//"}), "d:\\ignore\\some\\dir"));
458 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"));
460 assert(mem.eql(u8, testResolveWindows([][]const u8{"c:/ignore", "d:\\a/b\\c/d", "\\e.exe"}), "D:\\e.exe"));
461 assert(mem.eql(u8, testResolveWindows([][]const u8{"c:/ignore", "c:/some/file"}), "C:\\some\\file"));
462 assert(mem.eql(u8, testResolveWindows([][]const u8{"d:/ignore", "d:some/dir//"}), "D:\\ignore\\some\\dir"));
435463 assert(mem.eql(u8, testResolveWindows([][]const u8{"//server/share", "..", "relative\\"}), "\\\\server\\share\\relative"));
436 assert(mem.eql(u8, testResolveWindows([][]const u8{"c:/", "//"}), "c:\\"));
437 assert(mem.eql(u8, testResolveWindows([][]const u8{"c:/", "//dir"}), "c:\\dir"));
464 assert(mem.eql(u8, testResolveWindows([][]const u8{"c:/", "//"}), "C:\\"));
465 assert(mem.eql(u8, testResolveWindows([][]const u8{"c:/", "//dir"}), "C:\\dir"));
438466 assert(mem.eql(u8, testResolveWindows([][]const u8{"c:/", "//server/share"}), "\\\\server\\share\\"));
439467 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"));
441469 assert(mem.eql(u8, testResolveWindows([][]const u8{"C:\\foo\\tmp.3\\", "..\\tmp.3\\cycles\\root.js"}),
442470 "C:\\foo\\tmp.3\\cycles\\root.js"));
443471}
......@@ -475,27 +503,27 @@ pub fn dirnameWindows(path: []const u8) -> []const u8 {
475503 if (path.len == 0)
476504 return path[0..0];
477505
478 const rootSlice = rootWindows(path);
479 if (path.len == rootSlice.len)
506 const root_slice = rootWindows(path);
507 if (path.len == root_slice.len)
480508 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
484512 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) {
487515 if (end_index == 0)
488516 return path[0..0];
489517 end_index -= 1;
490518 }
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) {
493521 if (end_index == 0)
494522 return path[0..0];
495523 end_index -= 1;
496524 }
497525
498 if (have_root_slash and end_index == rootSlice.len) {
526 if (have_root_slash and end_index == root_slice.len) {
499527 end_index += 1;
500528 }
501529
......@@ -645,8 +673,8 @@ fn posixRelative(allocator: &Allocator, from: []const u8, to: []const u8) -> %[]
645673 const resolved_to = %return resolve(allocator, to);
646674 defer allocator.free(resolved_to);
647675
648 var from_it = mem.split(resolved_from, '/');
649 var to_it = mem.split(resolved_to, '/');
676 var from_it = mem.split(resolved_from, "/");
677 var to_it = mem.split(resolved_to, "/");
650678 while (true) {
651679 const from_component = from_it.next() ?? return mem.dupe(allocator, u8, to_it.rest());
652680 const to_rest = to_it.rest();