| author | |
| committer | |
| log | 08447ca47ed2ece816de9b0c5735be3f17edc6ca |
| tree | 99b9fa99c03f259c31e7673d5515ae20aff07b5d |
| parent | b64491f2d6e7b6d6d643ce1eff6d24873e414f08 |
Instead of querying the operating system for current working directory
and environment variables, this function now accepts those things as
inputs.15 files changed, 409 insertions(+), 432 deletions(-)
lib/compiler/build_runner.zig+1| ... | @@ -84,6 +84,7 @@ pub fn main(init: process.Init.Minimal) !void { | ... | @@ -84,6 +84,7 @@ pub fn main(init: process.Init.Minimal) !void { |
| 84 | .io = io, | 84 | .io = io, |
| 85 | .gpa = arena, | 85 | .gpa = arena, |
| 86 | .manifest_dir = try local_cache_directory.handle.createDirPathOpen(io, "h", .{}), | 86 | .manifest_dir = try local_cache_directory.handle.createDirPathOpen(io, "h", .{}), |
| 87 | .cwd = try process.getCwdAlloc(single_threaded_arena.allocator()), | ||
| 87 | }, | 88 | }, |
| 88 | .zig_exe = zig_exe, | 89 | .zig_exe = zig_exe, |
| 89 | .env_map = try init.environ.createMap(arena), | 90 | .env_map = try init.environ.createMap(arena), |
lib/compiler/test_runner.zig+1-1| ... | @@ -38,7 +38,7 @@ pub fn main(init: std.process.Init.Minimal) void { | ... | @@ -38,7 +38,7 @@ pub fn main(init: std.process.Init.Minimal) void { |
| 38 | } | 38 | } |
| 39 | 39 | ||
| 40 | if (need_simple) { | 40 | if (need_simple) { |
| 41 | return mainSimple() catch @panic("test failure\n"); | 41 | return mainSimple() catch @panic("test failure"); |
| 42 | } | 42 | } |
| 43 | 43 | ||
| 44 | const args = init.args.toSlice(fba.allocator()) catch @panic("unable to parse command line args"); | 44 | const args = init.args.toSlice(fba.allocator()) catch @panic("unable to parse command line args"); |
lib/std/Build.zig+1-2| ... | @@ -1738,8 +1738,7 @@ pub fn pathFromRoot(b: *Build, sub_path: []const u8) []u8 { | ... | @@ -1738,8 +1738,7 @@ pub fn pathFromRoot(b: *Build, sub_path: []const u8) []u8 { |
| 1738 | } | 1738 | } |
| 1739 | 1739 | ||
| 1740 | fn pathFromCwd(b: *Build, sub_path: []const u8) []u8 { | 1740 | fn pathFromCwd(b: *Build, sub_path: []const u8) []u8 { |
| 1741 | const cwd = process.getCwdAlloc(b.allocator) catch @panic("OOM"); | 1741 | return b.pathResolve(&.{ b.graph.cache.cwd, sub_path }); |
| 1742 | return b.pathResolve(&.{ cwd, sub_path }); | ||
| 1743 | } | 1742 | } |
| 1744 | 1743 | ||
| 1745 | pub fn pathJoin(b: *Build, paths: []const []const u8) []u8 { | 1744 | pub fn pathJoin(b: *Build, paths: []const []const u8) []u8 { |
lib/std/Build/Cache.zig+27-8| ... | @@ -30,6 +30,8 @@ mutex: Io.Mutex = .init, | ... | @@ -30,6 +30,8 @@ mutex: Io.Mutex = .init, |
| 30 | /// and usefulness of the cache for advanced use cases. | 30 | /// and usefulness of the cache for advanced use cases. |
| 31 | prefixes_buffer: [4]Directory = undefined, | 31 | prefixes_buffer: [4]Directory = undefined, |
| 32 | prefixes_len: usize = 0, | 32 | prefixes_len: usize = 0, |
| 33 | /// Used to identify prefixes. References external memory. | ||
| 34 | cwd: []const u8, | ||
| 33 | 35 | ||
| 34 | pub const Path = @import("Cache/Path.zig"); | 36 | pub const Path = @import("Cache/Path.zig"); |
| 35 | pub const Directory = @import("Cache/Directory.zig"); | 37 | pub const Directory = @import("Cache/Directory.zig"); |
| ... | @@ -78,11 +80,12 @@ fn findPrefix(cache: *const Cache, file_path: []const u8) !PrefixedPath { | ... | @@ -78,11 +80,12 @@ fn findPrefix(cache: *const Cache, file_path: []const u8) !PrefixedPath { |
| 78 | /// Takes ownership of `resolved_path` on success. | 80 | /// Takes ownership of `resolved_path` on success. |
| 79 | fn findPrefixResolved(cache: *const Cache, resolved_path: []u8) !PrefixedPath { | 81 | fn findPrefixResolved(cache: *const Cache, resolved_path: []u8) !PrefixedPath { |
| 80 | const gpa = cache.gpa; | 82 | const gpa = cache.gpa; |
| 83 | const cwd = cache.cwd; | ||
| 81 | const prefixes_slice = cache.prefixes(); | 84 | const prefixes_slice = cache.prefixes(); |
| 82 | var i: u8 = 1; // Start at 1 to skip over checking the null prefix. | 85 | var i: u8 = 1; // Start at 1 to skip over checking the null prefix. |
| 83 | while (i < prefixes_slice.len) : (i += 1) { | 86 | while (i < prefixes_slice.len) : (i += 1) { |
| 84 | const p = prefixes_slice[i].path.?; | 87 | const p = prefixes_slice[i].path.?; |
| 85 | const sub_path = getPrefixSubpath(gpa, p, resolved_path) catch |err| switch (err) { | 88 | const sub_path = getPrefixSubpath(gpa, cwd, p, resolved_path) catch |err| switch (err) { |
| 86 | error.NotASubPath => continue, | 89 | error.NotASubPath => continue, |
| 87 | else => |e| return e, | 90 | else => |e| return e, |
| 88 | }; | 91 | }; |
| ... | @@ -100,10 +103,10 @@ fn findPrefixResolved(cache: *const Cache, resolved_path: []u8) !PrefixedPath { | ... | @@ -100,10 +103,10 @@ fn findPrefixResolved(cache: *const Cache, resolved_path: []u8) !PrefixedPath { |
| 100 | }; | 103 | }; |
| 101 | } | 104 | } |
| 102 | 105 | ||
| 103 | fn getPrefixSubpath(allocator: Allocator, prefix: []const u8, path: []u8) ![]u8 { | 106 | fn getPrefixSubpath(gpa: Allocator, cwd: []const u8, prefix: []const u8, path: []u8) ![]u8 { |
| 104 | const relative = try std.fs.path.relative(allocator, prefix, path); | 107 | const relative = try std.fs.path.relative(gpa, cwd, null, prefix, path); |
| 105 | errdefer allocator.free(relative); | 108 | errdefer gpa.free(relative); |
| 106 | var component_iterator = std.fs.path.NativeComponentIterator.init(relative); | 109 | var component_iterator: std.fs.path.NativeComponentIterator = .init(relative); |
| 107 | if (component_iterator.root() != null) { | 110 | if (component_iterator.root() != null) { |
| 108 | return error.NotASubPath; | 111 | return error.NotASubPath; |
| 109 | } | 112 | } |
| ... | @@ -1307,11 +1310,14 @@ fn testGetCurrentFileTimestamp(io: Io, dir: Io.Dir) !Io.Timestamp { | ... | @@ -1307,11 +1310,14 @@ fn testGetCurrentFileTimestamp(io: Io, dir: Io.Dir) !Io.Timestamp { |
| 1307 | } | 1310 | } |
| 1308 | 1311 | ||
| 1309 | test "cache file and then recall it" { | 1312 | test "cache file and then recall it" { |
| 1310 | const io = std.testing.io; | 1313 | const io = testing.io; |
| 1311 | 1314 | ||
| 1312 | var tmp = testing.tmpDir(.{}); | 1315 | var tmp = testing.tmpDir(.{}); |
| 1313 | defer tmp.cleanup(); | 1316 | defer tmp.cleanup(); |
| 1314 | 1317 | ||
| 1318 | const cwd = try std.process.getCwdAlloc(testing.allocator); | ||
| 1319 | defer testing.allocator.free(cwd); | ||
| 1320 | |||
| 1315 | const temp_file = "test.txt"; | 1321 | const temp_file = "test.txt"; |
| 1316 | const temp_manifest_dir = "temp_manifest_dir"; | 1322 | const temp_manifest_dir = "temp_manifest_dir"; |
| 1317 | 1323 | ||
| ... | @@ -1331,6 +1337,7 @@ test "cache file and then recall it" { | ... | @@ -1331,6 +1337,7 @@ test "cache file and then recall it" { |
| 1331 | .io = io, | 1337 | .io = io, |
| 1332 | .gpa = testing.allocator, | 1338 | .gpa = testing.allocator, |
| 1333 | .manifest_dir = try tmp.dir.createDirPathOpen(io, temp_manifest_dir, .{}), | 1339 | .manifest_dir = try tmp.dir.createDirPathOpen(io, temp_manifest_dir, .{}), |
| 1340 | .cwd = cwd, | ||
| 1334 | }; | 1341 | }; |
| 1335 | cache.addPrefix(.{ .path = null, .handle = tmp.dir }); | 1342 | cache.addPrefix(.{ .path = null, .handle = tmp.dir }); |
| 1336 | defer cache.manifest_dir.close(io); | 1343 | defer cache.manifest_dir.close(io); |
| ... | @@ -1371,11 +1378,14 @@ test "cache file and then recall it" { | ... | @@ -1371,11 +1378,14 @@ test "cache file and then recall it" { |
| 1371 | } | 1378 | } |
| 1372 | 1379 | ||
| 1373 | test "check that changing a file makes cache fail" { | 1380 | test "check that changing a file makes cache fail" { |
| 1374 | const io = std.testing.io; | 1381 | const io = testing.io; |
| 1375 | 1382 | ||
| 1376 | var tmp = testing.tmpDir(.{}); | 1383 | var tmp = testing.tmpDir(.{}); |
| 1377 | defer tmp.cleanup(); | 1384 | defer tmp.cleanup(); |
| 1378 | 1385 | ||
| 1386 | const cwd = try std.process.getCwdAlloc(testing.allocator); | ||
| 1387 | defer testing.allocator.free(cwd); | ||
| 1388 | |||
| 1379 | const temp_file = "cache_hash_change_file_test.txt"; | 1389 | const temp_file = "cache_hash_change_file_test.txt"; |
| 1380 | const temp_manifest_dir = "cache_hash_change_file_manifest_dir"; | 1390 | const temp_manifest_dir = "cache_hash_change_file_manifest_dir"; |
| 1381 | const original_temp_file_contents = "Hello, world!\n"; | 1391 | const original_temp_file_contents = "Hello, world!\n"; |
| ... | @@ -1397,6 +1407,7 @@ test "check that changing a file makes cache fail" { | ... | @@ -1397,6 +1407,7 @@ test "check that changing a file makes cache fail" { |
| 1397 | .io = io, | 1407 | .io = io, |
| 1398 | .gpa = testing.allocator, | 1408 | .gpa = testing.allocator, |
| 1399 | .manifest_dir = try tmp.dir.createDirPathOpen(io, temp_manifest_dir, .{}), | 1409 | .manifest_dir = try tmp.dir.createDirPathOpen(io, temp_manifest_dir, .{}), |
| 1410 | .cwd = cwd, | ||
| 1400 | }; | 1411 | }; |
| 1401 | cache.addPrefix(.{ .path = null, .handle = tmp.dir }); | 1412 | cache.addPrefix(.{ .path = null, .handle = tmp.dir }); |
| 1402 | defer cache.manifest_dir.close(io); | 1413 | defer cache.manifest_dir.close(io); |
| ... | @@ -1448,6 +1459,9 @@ test "no file inputs" { | ... | @@ -1448,6 +1459,9 @@ test "no file inputs" { |
| 1448 | var tmp = testing.tmpDir(.{}); | 1459 | var tmp = testing.tmpDir(.{}); |
| 1449 | defer tmp.cleanup(); | 1460 | defer tmp.cleanup(); |
| 1450 | 1461 | ||
| 1462 | const cwd = try std.process.getCwdAlloc(testing.allocator); | ||
| 1463 | defer testing.allocator.free(cwd); | ||
| 1464 | |||
| 1451 | const temp_manifest_dir = "no_file_inputs_manifest_dir"; | 1465 | const temp_manifest_dir = "no_file_inputs_manifest_dir"; |
| 1452 | 1466 | ||
| 1453 | var digest1: HexDigest = undefined; | 1467 | var digest1: HexDigest = undefined; |
| ... | @@ -1457,6 +1471,7 @@ test "no file inputs" { | ... | @@ -1457,6 +1471,7 @@ test "no file inputs" { |
| 1457 | .io = io, | 1471 | .io = io, |
| 1458 | .gpa = testing.allocator, | 1472 | .gpa = testing.allocator, |
| 1459 | .manifest_dir = try tmp.dir.createDirPathOpen(io, temp_manifest_dir, .{}), | 1473 | .manifest_dir = try tmp.dir.createDirPathOpen(io, temp_manifest_dir, .{}), |
| 1474 | .cwd = cwd, | ||
| 1460 | }; | 1475 | }; |
| 1461 | cache.addPrefix(.{ .path = null, .handle = tmp.dir }); | 1476 | cache.addPrefix(.{ .path = null, .handle = tmp.dir }); |
| 1462 | defer cache.manifest_dir.close(io); | 1477 | defer cache.manifest_dir.close(io); |
| ... | @@ -1489,11 +1504,14 @@ test "no file inputs" { | ... | @@ -1489,11 +1504,14 @@ test "no file inputs" { |
| 1489 | } | 1504 | } |
| 1490 | 1505 | ||
| 1491 | test "Manifest with files added after initial hash work" { | 1506 | test "Manifest with files added after initial hash work" { |
| 1492 | const io = std.testing.io; | 1507 | const io = testing.io; |
| 1493 | 1508 | ||
| 1494 | var tmp = testing.tmpDir(.{}); | 1509 | var tmp = testing.tmpDir(.{}); |
| 1495 | defer tmp.cleanup(); | 1510 | defer tmp.cleanup(); |
| 1496 | 1511 | ||
| 1512 | const cwd = try std.process.getCwdAlloc(testing.allocator); | ||
| 1513 | defer testing.allocator.free(cwd); | ||
| 1514 | |||
| 1497 | const temp_file1 = "cache_hash_post_file_test1.txt"; | 1515 | const temp_file1 = "cache_hash_post_file_test1.txt"; |
| 1498 | const temp_file2 = "cache_hash_post_file_test2.txt"; | 1516 | const temp_file2 = "cache_hash_post_file_test2.txt"; |
| 1499 | const temp_manifest_dir = "cache_hash_post_file_manifest_dir"; | 1517 | const temp_manifest_dir = "cache_hash_post_file_manifest_dir"; |
| ... | @@ -1516,6 +1534,7 @@ test "Manifest with files added after initial hash work" { | ... | @@ -1516,6 +1534,7 @@ test "Manifest with files added after initial hash work" { |
| 1516 | .io = io, | 1534 | .io = io, |
| 1517 | .gpa = testing.allocator, | 1535 | .gpa = testing.allocator, |
| 1518 | .manifest_dir = try tmp.dir.createDirPathOpen(io, temp_manifest_dir, .{}), | 1536 | .manifest_dir = try tmp.dir.createDirPathOpen(io, temp_manifest_dir, .{}), |
| 1537 | .cwd = cwd, | ||
| 1519 | }; | 1538 | }; |
| 1520 | cache.addPrefix(.{ .path = null, .handle = tmp.dir }); | 1539 | cache.addPrefix(.{ .path = null, .handle = tmp.dir }); |
| 1521 | defer cache.manifest_dir.close(io); | 1540 | defer cache.manifest_dir.close(io); |
lib/std/Build/Step/Options.zig+4| ... | @@ -537,6 +537,9 @@ test Options { | ... | @@ -537,6 +537,9 @@ test Options { |
| 537 | var arena = std.heap.ArenaAllocator.init(std.testing.allocator); | 537 | var arena = std.heap.ArenaAllocator.init(std.testing.allocator); |
| 538 | defer arena.deinit(); | 538 | defer arena.deinit(); |
| 539 | 539 | ||
| 540 | const cwd = try std.process.getCwdAlloc(std.testing.allocator); | ||
| 541 | defer std.testing.allocator.free(cwd); | ||
| 542 | |||
| 540 | var graph: std.Build.Graph = .{ | 543 | var graph: std.Build.Graph = .{ |
| 541 | .io = io, | 544 | .io = io, |
| 542 | .arena = arena.allocator(), | 545 | .arena = arena.allocator(), |
| ... | @@ -544,6 +547,7 @@ test Options { | ... | @@ -544,6 +547,7 @@ test Options { |
| 544 | .io = io, | 547 | .io = io, |
| 545 | .gpa = arena.allocator(), | 548 | .gpa = arena.allocator(), |
| 546 | .manifest_dir = Io.Dir.cwd(), | 549 | .manifest_dir = Io.Dir.cwd(), |
| 550 | .cwd = cwd, | ||
| 547 | }, | 551 | }, |
| 548 | .zig_exe = "test", | 552 | .zig_exe = "test", |
| 549 | .env_map = std.process.Environ.Map.init(arena.allocator()), | 553 | .env_map = std.process.Environ.Map.init(arena.allocator()), |
lib/std/Build/Step/Run.zig+9-7| ... | @@ -750,28 +750,30 @@ fn checksContainStderr(checks: []const StdIo.Check) bool { | ... | @@ -750,28 +750,30 @@ fn checksContainStderr(checks: []const StdIo.Check) bool { |
| 750 | /// to make sure the child doesn't see paths relative to a cwd other than its own. | 750 | /// to make sure the child doesn't see paths relative to a cwd other than its own. |
| 751 | fn convertPathArg(run: *Run, path: Build.Cache.Path) []const u8 { | 751 | fn convertPathArg(run: *Run, path: Build.Cache.Path) []const u8 { |
| 752 | const b = run.step.owner; | 752 | const b = run.step.owner; |
| 753 | const path_str = path.toString(b.graph.arena) catch @panic("OOM"); | 753 | const graph = b.graph; |
| 754 | const arena = graph.arena; | ||
| 755 | |||
| 756 | const path_str = path.toString(arena) catch @panic("OOM"); | ||
| 754 | if (Dir.path.isAbsolute(path_str)) { | 757 | if (Dir.path.isAbsolute(path_str)) { |
| 755 | // Absolute paths don't need changing. | 758 | // Absolute paths don't need changing. |
| 756 | return path_str; | 759 | return path_str; |
| 757 | } | 760 | } |
| 758 | const child_cwd_rel: []const u8 = rel: { | 761 | const child_cwd_rel: []const u8 = rel: { |
| 759 | const child_lazy_cwd = run.cwd orelse break :rel path_str; | 762 | const child_lazy_cwd = run.cwd orelse break :rel path_str; |
| 760 | const child_cwd = child_lazy_cwd.getPath3(b, &run.step).toString(b.graph.arena) catch @panic("OOM"); | 763 | const child_cwd = child_lazy_cwd.getPath3(b, &run.step).toString(arena) catch @panic("OOM"); |
| 761 | // Convert it from relative to *our* cwd, to relative to the *child's* cwd. | 764 | // Convert it from relative to *our* cwd, to relative to the *child's* cwd. |
| 762 | break :rel Dir.path.relative(b.graph.arena, child_cwd, path_str) catch @panic("OOM"); | 765 | break :rel Dir.path.relative(arena, graph.cache.cwd, &graph.env_map, child_cwd, path_str) catch @panic("OOM"); |
| 763 | }; | 766 | }; |
| 764 | // Not every path can be made relative, e.g. if the path and the child cwd are on different | 767 | // Not every path can be made relative, e.g. if the path and the child cwd are on different |
| 765 | // disk designators on Windows. In that case, `relative` will return an absolute path which we can | 768 | // disk designators on Windows. In that case, `relative` will return an absolute path which we can |
| 766 | // just return. | 769 | // just return. |
| 767 | if (Dir.path.isAbsolute(child_cwd_rel)) { | 770 | if (Dir.path.isAbsolute(child_cwd_rel)) return child_cwd_rel; |
| 768 | return child_cwd_rel; | 771 | |
| 769 | } | ||
| 770 | // We're not done yet. In some cases this path must be prefixed with './': | 772 | // We're not done yet. In some cases this path must be prefixed with './': |
| 771 | // * On POSIX, the executable name cannot be a single component like 'foo' | 773 | // * On POSIX, the executable name cannot be a single component like 'foo' |
| 772 | // * Some executables might treat a leading '-' like a flag, which we must avoid | 774 | // * Some executables might treat a leading '-' like a flag, which we must avoid |
| 773 | // There's no harm in it, so just *always* apply this prefix. | 775 | // There's no harm in it, so just *always* apply this prefix. |
| 774 | return Dir.path.join(b.graph.arena, &.{ ".", child_cwd_rel }) catch @panic("OOM"); | 776 | return Dir.path.join(arena, &.{ ".", child_cwd_rel }) catch @panic("OOM"); |
| 775 | } | 777 | } |
| 776 | 778 | ||
| 777 | const IndexedOutput = struct { | 779 | const IndexedOutput = struct { |
lib/std/Build/Watch/FsEvents.zig+7-5| ... | @@ -43,6 +43,8 @@ dispatch_queue: dispatch_queue_t, | ... | @@ -43,6 +43,8 @@ dispatch_queue: dispatch_queue_t, |
| 43 | /// of writing. See the comment at the start of `wait` for details. | 43 | /// of writing. See the comment at the start of `wait` for details. |
| 44 | since_event: FSEventStreamEventId, | 44 | since_event: FSEventStreamEventId, |
| 45 | 45 | ||
| 46 | cwd_path: []const u8, | ||
| 47 | |||
| 46 | /// All of the symbols we pull from the `dlopen`ed CoreServices framework. If any of these symbols | 48 | /// All of the symbols we pull from the `dlopen`ed CoreServices framework. If any of these symbols |
| 47 | /// is not present, `init` will close the framework and return an error. | 49 | /// is not present, `init` will close the framework and return an error. |
| 48 | const ResolvedSymbols = struct { | 50 | const ResolvedSymbols = struct { |
| ... | @@ -78,7 +80,7 @@ const ResolvedSymbols = struct { | ... | @@ -78,7 +80,7 @@ const ResolvedSymbols = struct { |
| 78 | kCFAllocatorUseContext: *const CFAllocatorRef, | 80 | kCFAllocatorUseContext: *const CFAllocatorRef, |
| 79 | }; | 81 | }; |
| 80 | 82 | ||
| 81 | pub fn init() error{ OpenFrameworkFailed, MissingCoreServicesSymbol }!FsEvents { | 83 | pub fn init(cwd_path: []const u8) error{ OpenFrameworkFailed, MissingCoreServicesSymbol }!FsEvents { |
| 82 | var core_services = std.DynLib.open("/System/Library/Frameworks/CoreServices.framework/CoreServices") catch | 84 | var core_services = std.DynLib.open("/System/Library/Frameworks/CoreServices.framework/CoreServices") catch |
| 83 | return error.OpenFrameworkFailed; | 85 | return error.OpenFrameworkFailed; |
| 84 | errdefer core_services.close(); | 86 | errdefer core_services.close(); |
| ... | @@ -99,6 +101,7 @@ pub fn init() error{ OpenFrameworkFailed, MissingCoreServicesSymbol }!FsEvents { | ... | @@ -99,6 +101,7 @@ pub fn init() error{ OpenFrameworkFailed, MissingCoreServicesSymbol }!FsEvents { |
| 99 | // Not `.since_now`, because this means we can init `FsEvents` *before* we do work in order | 101 | // Not `.since_now`, because this means we can init `FsEvents` *before* we do work in order |
| 100 | // to notice any changes which happened during said work. | 102 | // to notice any changes which happened during said work. |
| 101 | .since_event = resolved_symbols.FSEventsGetCurrentEventId(), | 103 | .since_event = resolved_symbols.FSEventsGetCurrentEventId(), |
| 104 | .cwd_path = cwd_path, | ||
| 102 | }; | 105 | }; |
| 103 | } | 106 | } |
| 104 | 107 | ||
| ... | @@ -120,9 +123,6 @@ pub fn setPaths(fse: *FsEvents, gpa: Allocator, steps: []const *std.Build.Step) | ... | @@ -120,9 +123,6 @@ pub fn setPaths(fse: *FsEvents, gpa: Allocator, steps: []const *std.Build.Step) |
| 120 | defer fse.paths_arena = paths_arena_instance.state; | 123 | defer fse.paths_arena = paths_arena_instance.state; |
| 121 | const paths_arena = paths_arena_instance.allocator(); | 124 | const paths_arena = paths_arena_instance.allocator(); |
| 122 | 125 | ||
| 123 | const cwd_path = try std.process.getCwdAlloc(gpa); | ||
| 124 | defer gpa.free(cwd_path); | ||
| 125 | |||
| 126 | var need_dirs: std.StringArrayHashMapUnmanaged(void) = .empty; | 126 | var need_dirs: std.StringArrayHashMapUnmanaged(void) = .empty; |
| 127 | defer need_dirs.deinit(gpa); | 127 | defer need_dirs.deinit(gpa); |
| 128 | 128 | ||
| ... | @@ -131,7 +131,9 @@ pub fn setPaths(fse: *FsEvents, gpa: Allocator, steps: []const *std.Build.Step) | ... | @@ -131,7 +131,9 @@ pub fn setPaths(fse: *FsEvents, gpa: Allocator, steps: []const *std.Build.Step) |
| 131 | // We take `step` by pointer for a slight memory optimization in a moment. | 131 | // We take `step` by pointer for a slight memory optimization in a moment. |
| 132 | for (steps) |*step| { | 132 | for (steps) |*step| { |
| 133 | for (step.*.inputs.table.keys(), step.*.inputs.table.values()) |path, *files| { | 133 | for (step.*.inputs.table.keys(), step.*.inputs.table.values()) |path, *files| { |
| 134 | const resolved_dir = try std.fs.path.resolvePosix(paths_arena, &.{ cwd_path, path.root_dir.path orelse ".", path.sub_path }); | 134 | const resolved_dir = try std.fs.path.resolvePosix(paths_arena, &.{ |
| 135 | fse.cwd_path, path.root_dir.path orelse ".", path.sub_path, | ||
| 136 | }); | ||
| 135 | try need_dirs.put(gpa, resolved_dir, {}); | 137 | try need_dirs.put(gpa, resolved_dir, {}); |
| 136 | for (files.items) |file_name| { | 138 | for (files.items) |file_name| { |
| 137 | const watch_path = if (std.mem.eql(u8, file_name, ".")) | 139 | const watch_path = if (std.mem.eql(u8, file_name, ".")) |
lib/std/Build/WebServer.zig+3-9| ... | @@ -482,8 +482,8 @@ pub fn serveFile( | ... | @@ -482,8 +482,8 @@ pub fn serveFile( |
| 482 | }); | 482 | }); |
| 483 | } | 483 | } |
| 484 | pub fn serveTarFile(ws: *WebServer, request: *http.Server.Request, paths: []const Cache.Path) !void { | 484 | pub fn serveTarFile(ws: *WebServer, request: *http.Server.Request, paths: []const Cache.Path) !void { |
| 485 | const gpa = ws.gpa; | 485 | const graph = ws.graph; |
| 486 | const io = ws.graph.io; | 486 | const io = graph.io; |
| 487 | 487 | ||
| 488 | var send_buffer: [0x4000]u8 = undefined; | 488 | var send_buffer: [0x4000]u8 = undefined; |
| 489 | var response = try request.respondStreaming(&send_buffer, .{ | 489 | var response = try request.respondStreaming(&send_buffer, .{ |
| ... | @@ -495,9 +495,6 @@ pub fn serveTarFile(ws: *WebServer, request: *http.Server.Request, paths: []cons | ... | @@ -495,9 +495,6 @@ pub fn serveTarFile(ws: *WebServer, request: *http.Server.Request, paths: []cons |
| 495 | }, | 495 | }, |
| 496 | }); | 496 | }); |
| 497 | 497 | ||
| 498 | var cached_cwd_path: ?[]const u8 = null; | ||
| 499 | defer if (cached_cwd_path) |p| gpa.free(p); | ||
| 500 | |||
| 501 | var archiver: std.tar.Writer = .{ .underlying_writer = &response.writer }; | 498 | var archiver: std.tar.Writer = .{ .underlying_writer = &response.writer }; |
| 502 | 499 | ||
| 503 | for (paths) |path| { | 500 | for (paths) |path| { |
| ... | @@ -516,10 +513,7 @@ pub fn serveTarFile(ws: *WebServer, request: *http.Server.Request, paths: []cons | ... | @@ -516,10 +513,7 @@ pub fn serveTarFile(ws: *WebServer, request: *http.Server.Request, paths: []cons |
| 516 | // resulting in modules named "" and "src". The compiler needs to tell the build system | 513 | // resulting in modules named "" and "src". The compiler needs to tell the build system |
| 517 | // about the module graph so that the build system can correctly encode this information in | 514 | // about the module graph so that the build system can correctly encode this information in |
| 518 | // the tar file. | 515 | // the tar file. |
| 519 | archiver.prefix = path.root_dir.path orelse cwd: { | 516 | archiver.prefix = path.root_dir.path orelse graph.cache.cwd; |
| 520 | if (cached_cwd_path == null) cached_cwd_path = try std.process.getCwdAlloc(gpa); | ||
| 521 | break :cwd cached_cwd_path.?; | ||
| 522 | }; | ||
| 523 | try archiver.writeFile(path.sub_path, &file_reader, @intCast(stat.mtime.toSeconds())); | 517 | try archiver.writeFile(path.sub_path, &file_reader, @intCast(stat.mtime.toSeconds())); |
| 524 | } | 518 | } |
| 525 | 519 |
lib/std/Io/Threaded.zig+8-7| ... | @@ -86,14 +86,14 @@ pub const Argv0 = switch (native_os) { | ... | @@ -86,14 +86,14 @@ pub const Argv0 = switch (native_os) { |
| 86 | 86 | ||
| 87 | const Environ = struct { | 87 | const Environ = struct { |
| 88 | /// Unmodified data directly from the OS. | 88 | /// Unmodified data directly from the OS. |
| 89 | block: process.Environ.Block = &.{}, | 89 | process_environ: process.Environ = .empty, |
| 90 | /// Protected by `mutex`. Determines whether the other fields have been | 90 | /// Protected by `mutex`. Determines whether the other fields have been |
| 91 | /// memoized based on `block`. | 91 | /// memoized based on `process_environ`. |
| 92 | initialized: bool = false, | 92 | initialized: bool = false, |
| 93 | /// Protected by `mutex`. Memoized based on `block`. Tracks whether the | 93 | /// Protected by `mutex`. Memoized based on `process_environ`. Tracks whether the |
| 94 | /// environment variables are present, ignoring their value. | 94 | /// environment variables are present, ignoring their value. |
| 95 | exist: Exist = .{}, | 95 | exist: Exist = .{}, |
| 96 | /// Protected by `mutex`. Memoized based on `block`. | 96 | /// Protected by `mutex`. Memoized based on `process_environ`. |
| 97 | string: String = .{}, | 97 | string: String = .{}, |
| 98 | /// ZIG_PROGRESS | 98 | /// ZIG_PROGRESS |
| 99 | zig_progress_handle: std.Progress.ParentFileError!u31 = error.EnvironmentVariableMissing, | 99 | zig_progress_handle: std.Progress.ParentFileError!u31 = error.EnvironmentVariableMissing, |
| ... | @@ -1186,7 +1186,7 @@ pub fn init( | ... | @@ -1186,7 +1186,7 @@ pub fn init( |
| 1186 | .have_signal_handler = false, | 1186 | .have_signal_handler = false, |
| 1187 | .argv0 = options.argv0, | 1187 | .argv0 = options.argv0, |
| 1188 | .worker_threads = .init(null), | 1188 | .worker_threads = .init(null), |
| 1189 | .environ = .{ .block = options.environ.block }, | 1189 | .environ = .{ .process_environ = options.environ }, |
| 1190 | .robust_cancel = options.robust_cancel, | 1190 | .robust_cancel = options.robust_cancel, |
| 1191 | }; | 1191 | }; |
| 1192 | 1192 | ||
| ... | @@ -12693,7 +12693,7 @@ fn scanEnviron(t: *Threaded) void { | ... | @@ -12693,7 +12693,7 @@ fn scanEnviron(t: *Threaded) void { |
| 12693 | comptime assert(@sizeOf(Environ.String) == 0); | 12693 | comptime assert(@sizeOf(Environ.String) == 0); |
| 12694 | } | 12694 | } |
| 12695 | } else { | 12695 | } else { |
| 12696 | for (t.environ.block) |opt_line| { | 12696 | for (t.environ.process_environ.block) |opt_line| { |
| 12697 | const line = opt_line.?; | 12697 | const line = opt_line.?; |
| 12698 | var line_i: usize = 0; | 12698 | var line_i: usize = 0; |
| 12699 | while (line[line_i] != 0 and line[line_i] != '=') : (line_i += 1) {} | 12699 | while (line[line_i] != 0 and line[line_i] != '=') : (line_i += 1) {} |
| ... | @@ -12837,7 +12837,7 @@ fn processSpawnPosix(userdata: ?*anyopaque, options: process.SpawnOptions) proce | ... | @@ -12837,7 +12837,7 @@ fn processSpawnPosix(userdata: ?*anyopaque, options: process.SpawnOptions) proce |
| 12837 | .zig_progress_fd = prog_fd, | 12837 | .zig_progress_fd = prog_fd, |
| 12838 | })).ptr; | 12838 | })).ptr; |
| 12839 | } | 12839 | } |
| 12840 | break :m (try process.Environ.createBlockPosix(.{ .block = t.environ.block }, arena, .{ | 12840 | break :m (try process.Environ.createBlockPosix(t.environ.process_environ, arena, .{ |
| 12841 | .zig_progress_fd = prog_fd, | 12841 | .zig_progress_fd = prog_fd, |
| 12842 | })).ptr; | 12842 | })).ptr; |
| 12843 | }; | 12843 | }; |
| ... | @@ -12934,6 +12934,7 @@ fn processSpawnPosix(userdata: ?*anyopaque, options: process.SpawnOptions) proce | ... | @@ -12934,6 +12934,7 @@ fn processSpawnPosix(userdata: ?*anyopaque, options: process.SpawnOptions) proce |
| 12934 | 12934 | ||
| 12935 | return .{ | 12935 | return .{ |
| 12936 | .id = pid, | 12936 | .id = pid, |
| 12937 | .thread_handle = {}, | ||
| 12937 | .stdin = switch (options.stdin) { | 12938 | .stdin = switch (options.stdin) { |
| 12938 | .pipe => .{ .handle = stdin_pipe[1] }, | 12939 | .pipe => .{ .handle = stdin_pipe[1] }, |
| 12939 | else => null, | 12940 | else => null, |
lib/std/fs/path.zig+277-147| ... | @@ -13,16 +13,15 @@ | ... | @@ -13,16 +13,15 @@ |
| 13 | //! https://github.com/WebAssembly/wasi-filesystem/issues/17#issuecomment-1430639353 | 13 | //! https://github.com/WebAssembly/wasi-filesystem/issues/17#issuecomment-1430639353 |
| 14 | 14 | ||
| 15 | const builtin = @import("builtin"); | 15 | const builtin = @import("builtin"); |
| 16 | const native_os = builtin.target.os.tag; | ||
| 17 | |||
| 16 | const std = @import("../std.zig"); | 18 | const std = @import("../std.zig"); |
| 17 | const debug = std.debug; | 19 | const assert = std.debug.assert; |
| 18 | const assert = debug.assert; | ||
| 19 | const testing = std.testing; | 20 | const testing = std.testing; |
| 20 | const mem = std.mem; | 21 | const mem = std.mem; |
| 21 | const ascii = std.ascii; | 22 | const Allocator = std.mem.Allocator; |
| 22 | const Allocator = mem.Allocator; | 23 | const eqlIgnoreCaseWtf8 = std.os.windows.eqlIgnoreCaseWtf8; |
| 23 | const windows = std.os.windows; | 24 | const eqlIgnoreCaseWtf16 = std.os.windows.eqlIgnoreCaseWtf16; |
| 24 | const process = std.process; | ||
| 25 | const native_os = builtin.target.os.tag; | ||
| 26 | 25 | ||
| 27 | pub const sep_windows: u8 = '\\'; | 26 | pub const sep_windows: u8 = '\\'; |
| 28 | pub const sep_posix: u8 = '/'; | 27 | pub const sep_posix: u8 = '/'; |
| ... | @@ -281,7 +280,7 @@ pub fn isAbsolute(path: []const u8) bool { | ... | @@ -281,7 +280,7 @@ pub fn isAbsolute(path: []const u8) bool { |
| 281 | } | 280 | } |
| 282 | 281 | ||
| 283 | fn isAbsoluteWindowsImpl(comptime T: type, path: []const T) bool { | 282 | fn isAbsoluteWindowsImpl(comptime T: type, path: []const T) bool { |
| 284 | return switch (windows.getWin32PathType(T, path)) { | 283 | return switch (getWin32PathType(T, path)) { |
| 285 | // Unambiguously absolute | 284 | // Unambiguously absolute |
| 286 | .drive_absolute, .unc_absolute, .local_device, .root_local_device => true, | 285 | .drive_absolute, .unc_absolute, .local_device, .root_local_device => true, |
| 287 | // Unambiguously relative | 286 | // Unambiguously relative |
| ... | @@ -515,13 +514,13 @@ test parsePathPosix { | ... | @@ -515,13 +514,13 @@ test parsePathPosix { |
| 515 | 514 | ||
| 516 | pub fn WindowsPath2(comptime T: type) type { | 515 | pub fn WindowsPath2(comptime T: type) type { |
| 517 | return struct { | 516 | return struct { |
| 518 | kind: windows.Win32PathType, | 517 | kind: Win32PathType, |
| 519 | root: []const T, | 518 | root: []const T, |
| 520 | }; | 519 | }; |
| 521 | } | 520 | } |
| 522 | 521 | ||
| 523 | pub fn parsePathWindows(comptime T: type, path: []const T) WindowsPath2(T) { | 522 | pub fn parsePathWindows(comptime T: type, path: []const T) WindowsPath2(T) { |
| 524 | const kind = windows.getWin32PathType(T, path); | 523 | const kind = getWin32PathType(T, path); |
| 525 | const root = root: switch (kind) { | 524 | const root = root: switch (kind) { |
| 526 | .drive_absolute, .drive_relative => { | 525 | .drive_absolute, .drive_relative => { |
| 527 | const drive_letter_len = getDriveLetter(T, path).len; | 526 | const drive_letter_len = getDriveLetter(T, path).len; |
| ... | @@ -731,7 +730,7 @@ fn parseUNC(comptime T: type, path: []const T) WindowsUNC(T) { | ... | @@ -731,7 +730,7 @@ fn parseUNC(comptime T: type, path: []const T) WindowsUNC(T) { |
| 731 | // For the share, there can be any number of path separators between the server | 730 | // For the share, there can be any number of path separators between the server |
| 732 | // and the share, so we want to skip over all of them instead of just looking for | 731 | // and the share, so we want to skip over all of them instead of just looking for |
| 733 | // the first one. | 732 | // the first one. |
| 734 | var it = std.mem.tokenizeAny(T, path[server_end + 1 ..], any_sep); | 733 | var it = mem.tokenizeAny(T, path[server_end + 1 ..], any_sep); |
| 735 | const share = it.next() orelse return .{ | 734 | const share = it.next() orelse return .{ |
| 736 | .server = path[2..server_end], | 735 | .server = path[2..server_end], |
| 737 | .sep_after_server = true, | 736 | .sep_after_server = true, |
| ... | @@ -803,8 +802,8 @@ const DiskDesignatorKind = enum { drive, unc }; | ... | @@ -803,8 +802,8 @@ const DiskDesignatorKind = enum { drive, unc }; |
| 803 | /// `p1` and `p2` are both assumed to be the `kind` provided. | 802 | /// `p1` and `p2` are both assumed to be the `kind` provided. |
| 804 | fn compareDiskDesignators(comptime T: type, kind: DiskDesignatorKind, p1: []const T, p2: []const T) bool { | 803 | fn compareDiskDesignators(comptime T: type, kind: DiskDesignatorKind, p1: []const T, p2: []const T) bool { |
| 805 | const eql = switch (T) { | 804 | const eql = switch (T) { |
| 806 | u8 => windows.eqlIgnoreCaseWtf8, | 805 | u8 => eqlIgnoreCaseWtf8, |
| 807 | u16 => windows.eqlIgnoreCaseWtf16, | 806 | u16 => eqlIgnoreCaseWtf16, |
| 808 | else => @compileError("only u8 (WTF-8) and u16 (WTF-16LE) is supported"), | 807 | else => @compileError("only u8 (WTF-8) and u16 (WTF-16LE) is supported"), |
| 809 | }; | 808 | }; |
| 810 | switch (kind) { | 809 | switch (kind) { |
| ... | @@ -1094,10 +1093,14 @@ pub fn resolveWindows(allocator: Allocator, paths: []const []const u8) Allocator | ... | @@ -1094,10 +1093,14 @@ pub fn resolveWindows(allocator: Allocator, paths: []const []const u8) Allocator |
| 1094 | } | 1093 | } |
| 1095 | 1094 | ||
| 1096 | /// This function is like a series of `cd` statements executed one after another. | 1095 | /// This function is like a series of `cd` statements executed one after another. |
| 1096 | /// | ||
| 1097 | /// It resolves "." and ".." to the best of its ability, but will not convert relative paths to | 1097 | /// It resolves "." and ".." to the best of its ability, but will not convert relative paths to |
| 1098 | /// an absolute path, use Io.Dir.realpath instead. | 1098 | /// an absolute path, use Io.Dir.realpath instead. |
| 1099 | /// | ||
| 1099 | /// ".." components may persist in the resolved path if the resolved path is relative. | 1100 | /// ".." components may persist in the resolved path if the resolved path is relative. |
| 1101 | /// | ||
| 1100 | /// The result does not have a trailing path separator. | 1102 | /// The result does not have a trailing path separator. |
| 1103 | /// | ||
| 1101 | /// This function does not perform any syscalls. Executing this series of path | 1104 | /// This function does not perform any syscalls. Executing this series of path |
| 1102 | /// lookups on the actual filesystem may produce different results due to | 1105 | /// lookups on the actual filesystem may produce different results due to |
| 1103 | /// symlinks. | 1106 | /// symlinks. |
| ... | @@ -1494,25 +1497,54 @@ fn testBasenameWindows(input: []const u8, expected_output: []const u8) !void { | ... | @@ -1494,25 +1497,54 @@ fn testBasenameWindows(input: []const u8, expected_output: []const u8) !void { |
| 1494 | try testing.expectEqualSlices(u8, expected_output, basenameWindows(input)); | 1497 | try testing.expectEqualSlices(u8, expected_output, basenameWindows(input)); |
| 1495 | } | 1498 | } |
| 1496 | 1499 | ||
| 1497 | pub const RelativeError = std.process.GetCwdAllocError; | 1500 | /// Returns the non-absolute path from `from` to `to`. |
| 1498 | 1501 | /// | |
| 1499 | /// Returns the relative path from `from` to `to`. If `from` and `to` each | 1502 | /// Other than memory allocation, this is a pure function; the result solely |
| 1500 | /// resolve to the same path (after calling `resolve` on each), a zero-length | 1503 | /// depends on the input parameters. |
| 1501 | /// string is returned. | 1504 | /// |
| 1502 | /// On Windows, the result is not guaranteed to be relative, as the paths may be | 1505 | /// If `from` and `to` each resolve to the same path (after calling `resolve` |
| 1503 | /// on different volumes. In that case, the result will be the canonicalized absolute | 1506 | /// on each), a zero-length string is returned. |
| 1504 | /// path of `to`. | 1507 | /// |
| 1505 | pub fn relative(allocator: Allocator, from: []const u8, to: []const u8) RelativeError![]u8 { | 1508 | /// See `relativePosix` and `relativeWindows` for operating system specific |
| 1509 | /// details and for how `env_map` is used. | ||
| 1510 | pub fn relative( | ||
| 1511 | gpa: Allocator, | ||
| 1512 | cwd: []const u8, | ||
| 1513 | env_map: ?*const std.process.Environ.Map, | ||
| 1514 | from: []const u8, | ||
| 1515 | to: []const u8, | ||
| 1516 | ) Allocator.Error![]u8 { | ||
| 1506 | if (native_os == .windows) { | 1517 | if (native_os == .windows) { |
| 1507 | return relativeWindows(allocator, from, to); | 1518 | return relativeWindows(gpa, cwd, env_map, from, to); |
| 1508 | } else { | 1519 | } else { |
| 1509 | return relativePosix(allocator, from, to); | 1520 | return relativePosix(gpa, cwd, from, to); |
| 1510 | } | 1521 | } |
| 1511 | } | 1522 | } |
| 1512 | 1523 | ||
| 1513 | pub fn relativeWindows(allocator: Allocator, from: []const u8, to: []const u8) ![]u8 { | 1524 | /// Returns the non-absolute path from `from` to `to` according to Windows rules. |
| 1514 | if (native_os != .windows) @compileError("this function relies on Windows-specific semantics"); | 1525 | /// |
| 1515 | 1526 | /// Other than memory allocation, this is a pure function; the result solely | |
| 1527 | /// depends on the input parameters. | ||
| 1528 | /// | ||
| 1529 | /// If `from` and `to` each resolve to the same path (after calling `resolve` | ||
| 1530 | /// on each), a zero-length string is returned. | ||
| 1531 | /// | ||
| 1532 | /// The result is not guaranteed to be relative, as the paths may be on | ||
| 1533 | /// different volumes. In that case, the result will be the canonicalized | ||
| 1534 | /// absolute path of `to`. | ||
| 1535 | /// | ||
| 1536 | /// Per-drive CWDs are stored in special semi-hidden environment variables of | ||
| 1537 | /// the format `=<drive-letter>:`, e.g. `=C:`. This type of CWD is purely a | ||
| 1538 | /// shell concept, so there's no guarantee that it'll be set or that it'll even | ||
| 1539 | /// be accurate. This is the only reason for the `env_map` parameter. `null` is | ||
| 1540 | /// treated equivalent to the environment variable missing. | ||
| 1541 | pub fn relativeWindows( | ||
| 1542 | gpa: Allocator, | ||
| 1543 | cwd: []const u8, | ||
| 1544 | env_map: ?*const std.process.Environ.Map, | ||
| 1545 | from: []const u8, | ||
| 1546 | to: []const u8, | ||
| 1547 | ) Allocator.Error![]u8 { | ||
| 1516 | const parsed_from = parsePathWindows(u8, from); | 1548 | const parsed_from = parsePathWindows(u8, from); |
| 1517 | const parsed_to = parsePathWindows(u8, to); | 1549 | const parsed_to = parsePathWindows(u8, to); |
| 1518 | 1550 | ||
| ... | @@ -1533,14 +1565,14 @@ pub fn relativeWindows(allocator: Allocator, from: []const u8, to: []const u8) ! | ... | @@ -1533,14 +1565,14 @@ pub fn relativeWindows(allocator: Allocator, from: []const u8, to: []const u8) ! |
| 1533 | }; | 1565 | }; |
| 1534 | 1566 | ||
| 1535 | if (result_is_always_to) { | 1567 | if (result_is_always_to) { |
| 1536 | return windowsResolveAgainstCwd(allocator, to, parsed_to); | 1568 | return windowsResolveAgainstCwd(gpa, cwd, env_map, to, parsed_to); |
| 1537 | } | 1569 | } |
| 1538 | 1570 | ||
| 1539 | const resolved_from = try windowsResolveAgainstCwd(allocator, from, parsed_from); | 1571 | const resolved_from = try windowsResolveAgainstCwd(gpa, cwd, env_map, from, parsed_from); |
| 1540 | defer allocator.free(resolved_from); | 1572 | defer gpa.free(resolved_from); |
| 1541 | var clean_up_resolved_to = true; | 1573 | var clean_up_resolved_to = true; |
| 1542 | const resolved_to = try windowsResolveAgainstCwd(allocator, to, parsed_to); | 1574 | const resolved_to = try windowsResolveAgainstCwd(gpa, cwd, env_map, to, parsed_to); |
| 1543 | defer if (clean_up_resolved_to) allocator.free(resolved_to); | 1575 | defer if (clean_up_resolved_to) gpa.free(resolved_to); |
| 1544 | 1576 | ||
| 1545 | const parsed_resolved_from = parsePathWindows(u8, resolved_from); | 1577 | const parsed_resolved_from = parsePathWindows(u8, resolved_from); |
| 1546 | const parsed_resolved_to = parsePathWindows(u8, resolved_to); | 1578 | const parsed_resolved_to = parsePathWindows(u8, resolved_to); |
| ... | @@ -1569,18 +1601,18 @@ pub fn relativeWindows(allocator: Allocator, from: []const u8, to: []const u8) ! | ... | @@ -1569,18 +1601,18 @@ pub fn relativeWindows(allocator: Allocator, from: []const u8, to: []const u8) ! |
| 1569 | var from_it = mem.tokenizeAny(u8, resolved_from[parsed_resolved_from.root.len..], "/\\"); | 1601 | var from_it = mem.tokenizeAny(u8, resolved_from[parsed_resolved_from.root.len..], "/\\"); |
| 1570 | var to_it = mem.tokenizeAny(u8, resolved_to[parsed_resolved_to.root.len..], "/\\"); | 1602 | var to_it = mem.tokenizeAny(u8, resolved_to[parsed_resolved_to.root.len..], "/\\"); |
| 1571 | while (true) { | 1603 | while (true) { |
| 1572 | const from_component = from_it.next() orelse return allocator.dupe(u8, to_it.rest()); | 1604 | const from_component = from_it.next() orelse return gpa.dupe(u8, to_it.rest()); |
| 1573 | const to_rest = to_it.rest(); | 1605 | const to_rest = to_it.rest(); |
| 1574 | if (to_it.next()) |to_component| { | 1606 | if (to_it.next()) |to_component| { |
| 1575 | if (windows.eqlIgnoreCaseWtf8(from_component, to_component)) | 1607 | if (eqlIgnoreCaseWtf8(from_component, to_component)) |
| 1576 | continue; | 1608 | continue; |
| 1577 | } | 1609 | } |
| 1578 | var up_index_end = "..".len; | 1610 | var up_index_end = "..".len; |
| 1579 | while (from_it.next()) |_| { | 1611 | while (from_it.next()) |_| { |
| 1580 | up_index_end += "\\..".len; | 1612 | up_index_end += "\\..".len; |
| 1581 | } | 1613 | } |
| 1582 | const result = try allocator.alloc(u8, up_index_end + @intFromBool(to_rest.len > 0) + to_rest.len); | 1614 | const result = try gpa.alloc(u8, up_index_end + @intFromBool(to_rest.len > 0) + to_rest.len); |
| 1583 | errdefer allocator.free(result); | 1615 | errdefer gpa.free(result); |
| 1584 | 1616 | ||
| 1585 | result[0..2].* = "..".*; | 1617 | result[0..2].* = "..".*; |
| 1586 | var result_index: usize = 2; | 1618 | var result_index: usize = 2; |
| ... | @@ -1597,85 +1629,60 @@ pub fn relativeWindows(allocator: Allocator, from: []const u8, to: []const u8) ! | ... | @@ -1597,85 +1629,60 @@ pub fn relativeWindows(allocator: Allocator, from: []const u8, to: []const u8) ! |
| 1597 | result_index += to_component.len; | 1629 | result_index += to_component.len; |
| 1598 | } | 1630 | } |
| 1599 | 1631 | ||
| 1600 | return allocator.realloc(result, result_index); | 1632 | return gpa.realloc(result, result_index); |
| 1601 | } | 1633 | } |
| 1602 | return [_]u8{}; | 1634 | return [_]u8{}; |
| 1603 | } | 1635 | } |
| 1604 | 1636 | ||
| 1605 | fn windowsResolveAgainstCwd(allocator: Allocator, path: []const u8, parsed: WindowsPath2(u8)) ![]u8 { | 1637 | fn windowsResolveAgainstCwd( |
| 1638 | gpa: Allocator, | ||
| 1639 | cwd: []const u8, | ||
| 1640 | env_map: ?*const std.process.Environ.Map, | ||
| 1641 | path: []const u8, | ||
| 1642 | parsed: WindowsPath2(u8), | ||
| 1643 | ) ![]u8 { | ||
| 1606 | // Space for 256 WTF-16 code units; potentially 3 WTF-8 bytes per WTF-16 code unit | 1644 | // Space for 256 WTF-16 code units; potentially 3 WTF-8 bytes per WTF-16 code unit |
| 1607 | var temp_allocator_state = std.heap.stackFallback(256 * 3, allocator); | 1645 | var temp_allocator_state = std.heap.stackFallback(256 * 3, gpa); |
| 1608 | return switch (parsed.kind) { | 1646 | return switch (parsed.kind) { |
| 1609 | .drive_absolute, | 1647 | .drive_absolute, |
| 1610 | .unc_absolute, | 1648 | .unc_absolute, |
| 1611 | .root_local_device, | 1649 | .root_local_device, |
| 1612 | .local_device, | 1650 | .local_device, |
| 1613 | => try resolveWindows(allocator, &.{path}), | 1651 | => try resolveWindows(gpa, &.{path}), |
| 1614 | .relative => blk: { | ||
| 1615 | const temp_allocator = temp_allocator_state.get(); | ||
| 1616 | 1652 | ||
| 1617 | const peb_cwd = windows.peb().ProcessParameters.CurrentDirectory.DosPath; | 1653 | .relative => try resolveWindows(gpa, &.{ cwd, path }), |
| 1618 | const cwd_w = (peb_cwd.Buffer.?)[0 .. peb_cwd.Length / 2]; | ||
| 1619 | 1654 | ||
| 1620 | const wtf8_len = std.unicode.calcWtf8Len(cwd_w); | ||
| 1621 | const wtf8_buf = try temp_allocator.alloc(u8, wtf8_len); | ||
| 1622 | defer temp_allocator.free(wtf8_buf); | ||
| 1623 | assert(std.unicode.wtf16LeToWtf8(wtf8_buf, cwd_w) == wtf8_len); | ||
| 1624 | |||
| 1625 | break :blk try resolveWindows(allocator, &.{ wtf8_buf, path }); | ||
| 1626 | }, | ||
| 1627 | .rooted => blk: { | 1655 | .rooted => blk: { |
| 1628 | const peb_cwd = windows.peb().ProcessParameters.CurrentDirectory.DosPath; | 1656 | const parsed_cwd = parsePathWindows(u8, cwd); |
| 1629 | const cwd_w = (peb_cwd.Buffer.?)[0 .. peb_cwd.Length / 2]; | ||
| 1630 | const parsed_cwd = parsePathWindows(u16, cwd_w); | ||
| 1631 | switch (parsed_cwd.kind) { | 1657 | switch (parsed_cwd.kind) { |
| 1632 | .drive_absolute => { | 1658 | .drive_absolute => { |
| 1633 | var drive_buf = "_:\\".*; | 1659 | var drive_buf = "_:\\".*; |
| 1634 | drive_buf[0] = @truncate(cwd_w[0]); | 1660 | drive_buf[0] = cwd[0]; |
| 1635 | break :blk try resolveWindows(allocator, &.{ &drive_buf, path }); | 1661 | break :blk try resolveWindows(gpa, &.{ &drive_buf, path }); |
| 1636 | }, | 1662 | }, |
| 1637 | .unc_absolute => { | 1663 | .unc_absolute => { |
| 1638 | const temp_allocator = temp_allocator_state.get(); | 1664 | break :blk try resolveWindows(gpa, &.{ parsed_cwd.root, path }); |
| 1639 | var root_buf = try temp_allocator.alloc(u8, parsed_cwd.root.len * 3); | ||
| 1640 | defer temp_allocator.free(root_buf); | ||
| 1641 | |||
| 1642 | const wtf8_len = std.unicode.wtf16LeToWtf8(root_buf, parsed_cwd.root); | ||
| 1643 | const root = root_buf[0..wtf8_len]; | ||
| 1644 | break :blk try resolveWindows(allocator, &.{ root, path }); | ||
| 1645 | }, | 1665 | }, |
| 1646 | // Effectively a malformed CWD, give up and just return a normalized path | 1666 | // Effectively a malformed CWD, give up and just return a normalized path |
| 1647 | else => break :blk try resolveWindows(allocator, &.{path}), | 1667 | else => break :blk try resolveWindows(gpa, &.{path}), |
| 1648 | } | 1668 | } |
| 1649 | }, | 1669 | }, |
| 1650 | .drive_relative => blk: { | 1670 | .drive_relative => blk: { |
| 1651 | const temp_allocator = temp_allocator_state.get(); | 1671 | const temp_allocator = temp_allocator_state.get(); |
| 1652 | const drive_cwd = drive_cwd: { | 1672 | const drive_cwd = drive_cwd: { |
| 1653 | const peb_cwd = windows.peb().ProcessParameters.CurrentDirectory.DosPath; | 1673 | const parsed_cwd = parsePathWindows(u8, cwd); |
| 1654 | const cwd_w = (peb_cwd.Buffer.?)[0 .. peb_cwd.Length / 2]; | ||
| 1655 | const parsed_cwd = parsePathWindows(u16, cwd_w); | ||
| 1656 | 1674 | ||
| 1657 | if (parsed_cwd.kind == .drive_absolute) { | 1675 | if (parsed_cwd.kind == .drive_absolute) { |
| 1658 | const drive_letter_w = parsed_cwd.root[0]; | 1676 | const drive_letter_w = parsed_cwd.root[0]; |
| 1659 | const drive_letters_match = drive_letter_w <= 0x7F and | 1677 | const drive_letters_match = drive_letter_w <= 0x7F and |
| 1660 | ascii.toUpper(@intCast(drive_letter_w)) == ascii.toUpper(parsed.root[0]); | 1678 | std.ascii.toUpper(@intCast(drive_letter_w)) == std.ascii.toUpper(parsed.root[0]); |
| 1661 | if (drive_letters_match) { | 1679 | if (drive_letters_match) |
| 1662 | const wtf8_len = std.unicode.calcWtf8Len(cwd_w); | 1680 | break :drive_cwd cwd; |
| 1663 | const wtf8_buf = try temp_allocator.alloc(u8, wtf8_len); | 1681 | |
| 1664 | assert(std.unicode.wtf16LeToWtf8(wtf8_buf, cwd_w) == wtf8_len); | 1682 | if (env_map) |m| { |
| 1665 | break :drive_cwd wtf8_buf[0..]; | 1683 | if (m.get(&.{ '=', parsed.root[0], ':' })) |v| { |
| 1666 | } | 1684 | break :drive_cwd try temp_allocator.dupe(u8, v); |
| 1667 | 1685 | } | |
| 1668 | // Per-drive CWD's are stored in special semi-hidden environment variables | ||
| 1669 | // of the format `=<drive-letter>:`, e.g. `=C:`. This type of CWD is | ||
| 1670 | // purely a shell concept, so there's no guarantee that it'll be set | ||
| 1671 | // or that it'll even be accurate. | ||
| 1672 | var key_buf = std.unicode.wtf8ToWtf16LeStringLiteral("=_:").*; | ||
| 1673 | key_buf[1] = parsed.root[0]; | ||
| 1674 | if (std.process.getenvW(&key_buf)) |drive_cwd_w| { | ||
| 1675 | const wtf8_len = std.unicode.calcWtf8Len(drive_cwd_w); | ||
| 1676 | const wtf8_buf = try temp_allocator.alloc(u8, wtf8_len); | ||
| 1677 | assert(std.unicode.wtf16LeToWtf8(wtf8_buf, drive_cwd_w) == wtf8_len); | ||
| 1678 | break :drive_cwd wtf8_buf[0..]; | ||
| 1679 | } | 1686 | } |
| 1680 | } | 1687 | } |
| 1681 | 1688 | ||
| ... | @@ -1686,16 +1693,20 @@ fn windowsResolveAgainstCwd(allocator: Allocator, path: []const u8, parsed: Wind | ... | @@ -1686,16 +1693,20 @@ fn windowsResolveAgainstCwd(allocator: Allocator, path: []const u8, parsed: Wind |
| 1686 | break :drive_cwd drive_buf; | 1693 | break :drive_cwd drive_buf; |
| 1687 | }; | 1694 | }; |
| 1688 | defer temp_allocator.free(drive_cwd); | 1695 | defer temp_allocator.free(drive_cwd); |
| 1689 | break :blk try resolveWindows(allocator, &.{ drive_cwd, path }); | 1696 | break :blk try resolveWindows(gpa, &.{ drive_cwd, path }); |
| 1690 | }, | 1697 | }, |
| 1691 | }; | 1698 | }; |
| 1692 | } | 1699 | } |
| 1693 | 1700 | ||
| 1694 | pub fn relativePosix(allocator: Allocator, from: []const u8, to: []const u8) ![]u8 { | 1701 | /// Returns the non-absolute path from `from` to `to` according to Windows rules. |
| 1695 | if (native_os == .windows) @compileError("this function relies on semantics that do not apply to Windows"); | 1702 | /// |
| 1696 | 1703 | /// Other than memory allocation, this is a pure function; the result solely | |
| 1697 | const cwd = try process.getCwdAlloc(allocator); | 1704 | /// depends on the input parameters. |
| 1698 | defer allocator.free(cwd); | 1705 | /// |
| 1706 | /// If `from` and `to` each resolve to the same path (after calling `resolve` | ||
| 1707 | /// on each), a zero-length string is returned. | ||
| 1708 | /// | ||
| 1709 | pub fn relativePosix(allocator: Allocator, cwd: []const u8, from: []const u8, to: []const u8) Allocator.Error![]u8 { | ||
| 1699 | const resolved_from = try resolvePosix(allocator, &[_][]const u8{ cwd, from }); | 1710 | const resolved_from = try resolvePosix(allocator, &[_][]const u8{ cwd, from }); |
| 1700 | defer allocator.free(resolved_from); | 1711 | defer allocator.free(resolved_from); |
| 1701 | const resolved_to = try resolvePosix(allocator, &[_][]const u8{ cwd, to }); | 1712 | const resolved_to = try resolvePosix(allocator, &[_][]const u8{ cwd, to }); |
| ... | @@ -1736,69 +1747,67 @@ pub fn relativePosix(allocator: Allocator, from: []const u8, to: []const u8) ![] | ... | @@ -1736,69 +1747,67 @@ pub fn relativePosix(allocator: Allocator, from: []const u8, to: []const u8) ![] |
| 1736 | } | 1747 | } |
| 1737 | 1748 | ||
| 1738 | test relative { | 1749 | test relative { |
| 1739 | if (native_os == .windows) { | 1750 | try testRelativeWindows("c:/blah\\blah", "d:/games", "D:\\games"); |
| 1740 | try testRelativeWindows("c:/blah\\blah", "d:/games", "D:\\games"); | 1751 | try testRelativeWindows("c:/aaaa/bbbb", "c:/aaaa", ".."); |
| 1741 | try testRelativeWindows("c:/aaaa/bbbb", "c:/aaaa", ".."); | 1752 | try testRelativeWindows("c:/aaaa/bbbb", "c:/cccc", "..\\..\\cccc"); |
| 1742 | try testRelativeWindows("c:/aaaa/bbbb", "c:/cccc", "..\\..\\cccc"); | 1753 | try testRelativeWindows("c:/aaaa/bbbb", "C:/aaaa/bbbb", ""); |
| 1743 | try testRelativeWindows("c:/aaaa/bbbb", "C:/aaaa/bbbb", ""); | 1754 | try testRelativeWindows("c:/aaaa/bbbb", "c:/aaaa/cccc", "..\\cccc"); |
| 1744 | try testRelativeWindows("c:/aaaa/bbbb", "c:/aaaa/cccc", "..\\cccc"); | 1755 | try testRelativeWindows("c:/aaaa/", "c:/aaaa/cccc", "cccc"); |
| 1745 | try testRelativeWindows("c:/aaaa/", "c:/aaaa/cccc", "cccc"); | 1756 | try testRelativeWindows("c:/", "c:\\aaaa\\bbbb", "aaaa\\bbbb"); |
| 1746 | try testRelativeWindows("c:/", "c:\\aaaa\\bbbb", "aaaa\\bbbb"); | 1757 | try testRelativeWindows("c:/aaaa/bbbb", "d:\\", "D:\\"); |
| 1747 | try testRelativeWindows("c:/aaaa/bbbb", "d:\\", "D:\\"); | 1758 | try testRelativeWindows("c:/AaAa/bbbb", "c:/aaaa/bbbb", ""); |
| 1748 | try testRelativeWindows("c:/AaAa/bbbb", "c:/aaaa/bbbb", ""); | 1759 | try testRelativeWindows("c:/aaaaa/", "c:/aaaa/cccc", "..\\aaaa\\cccc"); |
| 1749 | try testRelativeWindows("c:/aaaaa/", "c:/aaaa/cccc", "..\\aaaa\\cccc"); | 1760 | try testRelativeWindows("C:\\foo\\bar\\baz\\quux", "C:\\", "..\\..\\..\\.."); |
| 1750 | try testRelativeWindows("C:\\foo\\bar\\baz\\quux", "C:\\", "..\\..\\..\\.."); | 1761 | try testRelativeWindows("C:\\foo\\test", "C:\\foo\\test\\bar\\package.json", "bar\\package.json"); |
| 1751 | try testRelativeWindows("C:\\foo\\test", "C:\\foo\\test\\bar\\package.json", "bar\\package.json"); | 1762 | try testRelativeWindows("C:\\foo\\bar\\baz-quux", "C:\\foo\\bar\\baz", "..\\baz"); |
| 1752 | try testRelativeWindows("C:\\foo\\bar\\baz-quux", "C:\\foo\\bar\\baz", "..\\baz"); | 1763 | try testRelativeWindows("C:\\foo\\bar\\baz", "C:\\foo\\bar\\baz-quux", "..\\baz-quux"); |
| 1753 | try testRelativeWindows("C:\\foo\\bar\\baz", "C:\\foo\\bar\\baz-quux", "..\\baz-quux"); | 1764 | try testRelativeWindows("\\\\foo\\bar", "\\\\foo\\bar\\baz", "baz"); |
| 1754 | try testRelativeWindows("\\\\foo\\bar", "\\\\foo\\bar\\baz", "baz"); | 1765 | try testRelativeWindows("\\\\foo\\bar\\baz", "\\\\foo\\bar", ".."); |
| 1755 | try testRelativeWindows("\\\\foo\\bar\\baz", "\\\\foo\\bar", ".."); | 1766 | try testRelativeWindows("\\\\foo\\bar\\baz-quux", "\\\\foo\\bar\\baz", "..\\baz"); |
| 1756 | try testRelativeWindows("\\\\foo\\bar\\baz-quux", "\\\\foo\\bar\\baz", "..\\baz"); | 1767 | try testRelativeWindows("\\\\foo/bar\\baz-quux", "//foo\\bar/baz", "..\\baz"); |
| 1757 | try testRelativeWindows("\\\\foo/bar\\baz-quux", "//foo\\bar/baz", "..\\baz"); | 1768 | try testRelativeWindows("\\\\foo\\bar\\baz", "\\\\foo\\bar\\baz-quux", "..\\baz-quux"); |
| 1758 | try testRelativeWindows("\\\\foo\\bar\\baz", "\\\\foo\\bar\\baz-quux", "..\\baz-quux"); | 1769 | try testRelativeWindows("C:\\baz-quux", "C:\\baz", "..\\baz"); |
| 1759 | try testRelativeWindows("C:\\baz-quux", "C:\\baz", "..\\baz"); | 1770 | try testRelativeWindows("C:\\baz", "C:\\baz-quux", "..\\baz-quux"); |
| 1760 | try testRelativeWindows("C:\\baz", "C:\\baz-quux", "..\\baz-quux"); | 1771 | try testRelativeWindows("\\\\foo\\baz-quux", "\\\\foo\\baz", "\\\\foo\\baz"); |
| 1761 | try testRelativeWindows("\\\\foo\\baz-quux", "\\\\foo\\baz", "\\\\foo\\baz"); | 1772 | try testRelativeWindows("\\\\foo\\baz", "\\\\foo\\baz-quux", "\\\\foo\\baz-quux"); |
| 1762 | try testRelativeWindows("\\\\foo\\baz", "\\\\foo\\baz-quux", "\\\\foo\\baz-quux"); | 1773 | try testRelativeWindows("C:\\baz", "\\\\foo\\bar\\baz", "\\\\foo\\bar\\baz"); |
| 1763 | try testRelativeWindows("C:\\baz", "\\\\foo\\bar\\baz", "\\\\foo\\bar\\baz"); | 1774 | try testRelativeWindows("\\\\foo\\bar\\baz", "C:\\baz", "C:\\baz"); |
| 1764 | try testRelativeWindows("\\\\foo\\bar\\baz", "C:\\baz", "C:\\baz"); | 1775 | |
| 1765 | 1776 | try testRelativeWindows("c:blah\\blah", "c:foo", "..\\..\\foo"); | |
| 1766 | try testRelativeWindows("c:blah\\blah", "c:foo", "..\\..\\foo"); | 1777 | try testRelativeWindows("c:foo", "c:foo\\bar", "bar"); |
| 1767 | try testRelativeWindows("c:foo", "c:foo\\bar", "bar"); | 1778 | try testRelativeWindows("\\blah\\blah", "\\foo", "..\\..\\foo"); |
| 1768 | try testRelativeWindows("\\blah\\blah", "\\foo", "..\\..\\foo"); | 1779 | try testRelativeWindows("\\foo", "\\foo\\bar", "bar"); |
| 1769 | try testRelativeWindows("\\foo", "\\foo\\bar", "bar"); | 1780 | |
| 1770 | 1781 | try testRelativeWindows("a/b/c", "a\\b", ".."); | |
| 1771 | try testRelativeWindows("a/b/c", "a\\b", ".."); | 1782 | try testRelativeWindows("a/b/c", "a", "..\\.."); |
| 1772 | try testRelativeWindows("a/b/c", "a", "..\\.."); | 1783 | try testRelativeWindows("a/b/c", "a\\b\\c\\d", "d"); |
| 1773 | try testRelativeWindows("a/b/c", "a\\b\\c\\d", "d"); | 1784 | |
| 1774 | 1785 | try testRelativeWindows("\\\\FOO\\bar\\baz", "\\\\foo\\BAR\\BAZ", ""); | |
| 1775 | try testRelativeWindows("\\\\FOO\\bar\\baz", "\\\\foo\\BAR\\BAZ", ""); | 1786 | // Unicode-aware case-insensitive path comparison |
| 1776 | // Unicode-aware case-insensitive path comparison | 1787 | try testRelativeWindows("\\\\кириллица\\ελληνικά\\português", "\\\\КИРИЛЛИЦА\\ΕΛΛΗΝΙΚΆ\\PORTUGUÊS", ""); |
| 1777 | try testRelativeWindows("\\\\кириллица\\ελληνικά\\português", "\\\\КИРИЛЛИЦА\\ΕΛΛΗΝΙΚΆ\\PORTUGUÊS", ""); | 1788 | |
| 1778 | } else { | 1789 | try testRelativePosix("/var/lib", "/var", ".."); |
| 1779 | try testRelativePosix("/var/lib", "/var", ".."); | 1790 | try testRelativePosix("/var/lib", "/bin", "../../bin"); |
| 1780 | try testRelativePosix("/var/lib", "/bin", "../../bin"); | 1791 | try testRelativePosix("/var/lib", "/var/lib", ""); |
| 1781 | try testRelativePosix("/var/lib", "/var/lib", ""); | 1792 | try testRelativePosix("/var/lib", "/var/apache", "../apache"); |
| 1782 | try testRelativePosix("/var/lib", "/var/apache", "../apache"); | 1793 | try testRelativePosix("/var/", "/var/lib", "lib"); |
| 1783 | try testRelativePosix("/var/", "/var/lib", "lib"); | 1794 | try testRelativePosix("/", "/var/lib", "var/lib"); |
| 1784 | try testRelativePosix("/", "/var/lib", "var/lib"); | 1795 | try testRelativePosix("/foo/test", "/foo/test/bar/package.json", "bar/package.json"); |
| 1785 | try testRelativePosix("/foo/test", "/foo/test/bar/package.json", "bar/package.json"); | 1796 | try testRelativePosix("/Users/a/web/b/test/mails", "/Users/a/web/b", "../.."); |
| 1786 | try testRelativePosix("/Users/a/web/b/test/mails", "/Users/a/web/b", "../.."); | 1797 | try testRelativePosix("/foo/bar/baz-quux", "/foo/bar/baz", "../baz"); |
| 1787 | try testRelativePosix("/foo/bar/baz-quux", "/foo/bar/baz", "../baz"); | 1798 | try testRelativePosix("/foo/bar/baz", "/foo/bar/baz-quux", "../baz-quux"); |
| 1788 | try testRelativePosix("/foo/bar/baz", "/foo/bar/baz-quux", "../baz-quux"); | 1799 | try testRelativePosix("/baz-quux", "/baz", "../baz"); |
| 1789 | try testRelativePosix("/baz-quux", "/baz", "../baz"); | 1800 | try testRelativePosix("/baz", "/baz-quux", "../baz-quux"); |
| 1790 | try testRelativePosix("/baz", "/baz-quux", "../baz-quux"); | ||
| 1791 | } | ||
| 1792 | } | 1801 | } |
| 1793 | 1802 | ||
| 1794 | fn testRelativePosix(from: []const u8, to: []const u8, expected_output: []const u8) !void { | 1803 | fn testRelativePosix(from: []const u8, to: []const u8, expected_output: []const u8) !void { |
| 1795 | const result = try relativePosix(testing.allocator, from, to); | 1804 | const result = try relativePosix(testing.allocator, ".", from, to); |
| 1796 | defer testing.allocator.free(result); | 1805 | defer testing.allocator.free(result); |
| 1797 | try testing.expectEqualStrings(expected_output, result); | 1806 | try testing.expectEqualStrings(expected_output, result); |
| 1798 | } | 1807 | } |
| 1799 | 1808 | ||
| 1800 | fn testRelativeWindows(from: []const u8, to: []const u8, expected_output: []const u8) !void { | 1809 | fn testRelativeWindows(from: []const u8, to: []const u8, expected_output: []const u8) !void { |
| 1801 | const result = try relativeWindows(testing.allocator, from, to); | 1810 | const result = try relativeWindows(testing.allocator, ".", null, from, to); |
| 1802 | defer testing.allocator.free(result); | 1811 | defer testing.allocator.free(result); |
| 1803 | try testing.expectEqualStrings(expected_output, result); | 1812 | try testing.expectEqualStrings(expected_output, result); |
| 1804 | } | 1813 | } |
| ... | @@ -2554,3 +2563,124 @@ pub const fmtAsUtf8Lossy = std.unicode.fmtUtf8; | ... | @@ -2554,3 +2563,124 @@ pub const fmtAsUtf8Lossy = std.unicode.fmtUtf8; |
| 2554 | /// a lossy conversion if the path contains any unpaired surrogates. | 2563 | /// a lossy conversion if the path contains any unpaired surrogates. |
| 2555 | /// Unpaired surrogates are replaced by the replacement character (U+FFFD). | 2564 | /// Unpaired surrogates are replaced by the replacement character (U+FFFD). |
| 2556 | pub const fmtWtf16LeAsUtf8Lossy = std.unicode.fmtUtf16Le; | 2565 | pub const fmtWtf16LeAsUtf8Lossy = std.unicode.fmtUtf16Le; |
| 2566 | |||
| 2567 | /// Similar to `RTL_PATH_TYPE`, but without the `UNKNOWN` path type. | ||
| 2568 | pub const Win32PathType = enum { | ||
| 2569 | /// `\\server\share\foo` | ||
| 2570 | unc_absolute, | ||
| 2571 | /// `C:\foo` | ||
| 2572 | drive_absolute, | ||
| 2573 | /// `C:foo` | ||
| 2574 | drive_relative, | ||
| 2575 | /// `\foo` | ||
| 2576 | rooted, | ||
| 2577 | /// `foo` | ||
| 2578 | relative, | ||
| 2579 | /// `\\.\foo`, `\\?\foo` | ||
| 2580 | local_device, | ||
| 2581 | /// `\\.`, `\\?` | ||
| 2582 | root_local_device, | ||
| 2583 | }; | ||
| 2584 | |||
| 2585 | /// Get the path type of a Win32 namespace path. | ||
| 2586 | /// Similar to `RtlDetermineDosPathNameType_U`. | ||
| 2587 | /// If `T` is `u16`, then `path` should be encoded as WTF-16LE. | ||
| 2588 | pub fn getWin32PathType(comptime T: type, path: []const T) Win32PathType { | ||
| 2589 | if (path.len < 1) return .relative; | ||
| 2590 | |||
| 2591 | const windows_path = std.fs.path.PathType.windows; | ||
| 2592 | if (windows_path.isSep(T, path[0])) { | ||
| 2593 | // \x | ||
| 2594 | if (path.len < 2 or !windows_path.isSep(T, path[1])) return .rooted; | ||
| 2595 | // \\. or \\? | ||
| 2596 | if (path.len > 2 and (path[2] == mem.nativeToLittle(T, '.') or path[2] == mem.nativeToLittle(T, '?'))) { | ||
| 2597 | // exactly \\. or \\? with nothing trailing | ||
| 2598 | if (path.len == 3) return .root_local_device; | ||
| 2599 | // \\.\x or \\?\x | ||
| 2600 | if (windows_path.isSep(T, path[3])) return .local_device; | ||
| 2601 | } | ||
| 2602 | // \\x | ||
| 2603 | return .unc_absolute; | ||
| 2604 | } else { | ||
| 2605 | // Some choice has to be made about how non-ASCII code points as drive-letters are handled, since | ||
| 2606 | // path[0] is a different size for WTF-16 vs WTF-8, leading to a potential mismatch in classification | ||
| 2607 | // for a WTF-8 path and its WTF-16 equivalent. For example, `€:\` encoded in WTF-16 is three code | ||
| 2608 | // units `<0x20AC>:\` whereas `€:\` encoded as WTF-8 is 6 code units `<0xE2><0x82><0xAC>:\` so | ||
| 2609 | // checking path[0], path[1] and path[2] would not behave the same between WTF-8/WTF-16. | ||
| 2610 | // | ||
| 2611 | // `RtlDetermineDosPathNameType_U` exclusively deals with WTF-16 and considers | ||
| 2612 | // `€:\` a drive-absolute path, but code points that take two WTF-16 code units to encode get | ||
| 2613 | // classified as a relative path (e.g. with U+20000 as the drive-letter that'd be encoded | ||
| 2614 | // in WTF-16 as `<0xD840><0xDC00>:\` and be considered a relative path). | ||
| 2615 | // | ||
| 2616 | // The choice made here is to emulate the behavior of `RtlDetermineDosPathNameType_U` for both | ||
| 2617 | // WTF-16 and WTF-8. This is because, while unlikely and not supported by the Disk Manager GUI, | ||
| 2618 | // drive letters are not actually restricted to A-Z. Using `SetVolumeMountPointW` will allow you | ||
| 2619 | // to set any byte value as a drive letter, and going through `IOCTL_MOUNTMGR_CREATE_POINT` will | ||
| 2620 | // allow you to set any WTF-16 code unit as a drive letter. | ||
| 2621 | // | ||
| 2622 | // Non-A-Z drive letters don't interact well with most of Windows, but certain things do work, e.g. | ||
| 2623 | // `cd /D €:\` will work, filesystem functions still work, etc. | ||
| 2624 | // | ||
| 2625 | // The unfortunate part of this is that this makes handling WTF-8 more complicated as we can't | ||
| 2626 | // just check path[0], path[1], path[2]. | ||
| 2627 | const colon_i: usize = switch (T) { | ||
| 2628 | u8 => i: { | ||
| 2629 | const code_point_len = std.unicode.utf8ByteSequenceLength(path[0]) catch return .relative; | ||
| 2630 | // Conveniently, 4-byte sequences in WTF-8 have the same starting code point | ||
| 2631 | // as 2-code-unit sequences in WTF-16. | ||
| 2632 | if (code_point_len > 3) return .relative; | ||
| 2633 | break :i code_point_len; | ||
| 2634 | }, | ||
| 2635 | u16 => 1, | ||
| 2636 | else => @compileError("unsupported type: " ++ @typeName(T)), | ||
| 2637 | }; | ||
| 2638 | // x | ||
| 2639 | if (path.len < colon_i + 1 or path[colon_i] != mem.nativeToLittle(T, ':')) return .relative; | ||
| 2640 | // x:\ | ||
| 2641 | if (path.len > colon_i + 1 and windows_path.isSep(T, path[colon_i + 1])) return .drive_absolute; | ||
| 2642 | // x: | ||
| 2643 | return .drive_relative; | ||
| 2644 | } | ||
| 2645 | } | ||
| 2646 | |||
| 2647 | test getWin32PathType { | ||
| 2648 | try std.testing.expectEqual(.relative, getWin32PathType(u8, "")); | ||
| 2649 | try std.testing.expectEqual(.relative, getWin32PathType(u8, "x")); | ||
| 2650 | try std.testing.expectEqual(.relative, getWin32PathType(u8, "x\\")); | ||
| 2651 | |||
| 2652 | try std.testing.expectEqual(.root_local_device, getWin32PathType(u8, "//.")); | ||
| 2653 | try std.testing.expectEqual(.root_local_device, getWin32PathType(u8, "/\\?")); | ||
| 2654 | try std.testing.expectEqual(.root_local_device, getWin32PathType(u8, "\\\\?")); | ||
| 2655 | |||
| 2656 | try std.testing.expectEqual(.local_device, getWin32PathType(u8, "//./x")); | ||
| 2657 | try std.testing.expectEqual(.local_device, getWin32PathType(u8, "/\\?\\x")); | ||
| 2658 | try std.testing.expectEqual(.local_device, getWin32PathType(u8, "\\\\?\\x")); | ||
| 2659 | // local device paths require a path separator after the root, otherwise it is considered a UNC path | ||
| 2660 | try std.testing.expectEqual(.unc_absolute, getWin32PathType(u8, "\\\\?x")); | ||
| 2661 | try std.testing.expectEqual(.unc_absolute, getWin32PathType(u8, "//.x")); | ||
| 2662 | |||
| 2663 | try std.testing.expectEqual(.unc_absolute, getWin32PathType(u8, "//")); | ||
| 2664 | try std.testing.expectEqual(.unc_absolute, getWin32PathType(u8, "\\\\x")); | ||
| 2665 | try std.testing.expectEqual(.unc_absolute, getWin32PathType(u8, "//x")); | ||
| 2666 | |||
| 2667 | try std.testing.expectEqual(.rooted, getWin32PathType(u8, "\\x")); | ||
| 2668 | try std.testing.expectEqual(.rooted, getWin32PathType(u8, "/")); | ||
| 2669 | |||
| 2670 | try std.testing.expectEqual(.drive_relative, getWin32PathType(u8, "x:")); | ||
| 2671 | try std.testing.expectEqual(.drive_relative, getWin32PathType(u8, "x:abc")); | ||
| 2672 | try std.testing.expectEqual(.drive_relative, getWin32PathType(u8, "x:a/b/c")); | ||
| 2673 | |||
| 2674 | try std.testing.expectEqual(.drive_absolute, getWin32PathType(u8, "x:\\")); | ||
| 2675 | try std.testing.expectEqual(.drive_absolute, getWin32PathType(u8, "x:\\abc")); | ||
| 2676 | try std.testing.expectEqual(.drive_absolute, getWin32PathType(u8, "x:/a/b/c")); | ||
| 2677 | |||
| 2678 | // Non-ASCII code point that is encoded as one WTF-16 code unit is considered a valid drive letter | ||
| 2679 | try std.testing.expectEqual(.drive_absolute, getWin32PathType(u8, "€:\\")); | ||
| 2680 | try std.testing.expectEqual(.drive_absolute, getWin32PathType(u16, std.unicode.wtf8ToWtf16LeStringLiteral("€:\\"))); | ||
| 2681 | try std.testing.expectEqual(.drive_relative, getWin32PathType(u8, "€:")); | ||
| 2682 | try std.testing.expectEqual(.drive_relative, getWin32PathType(u16, std.unicode.wtf8ToWtf16LeStringLiteral("€:"))); | ||
| 2683 | // But code points that are encoded as two WTF-16 code units are not | ||
| 2684 | try std.testing.expectEqual(.relative, getWin32PathType(u8, "\u{10000}:\\")); | ||
| 2685 | try std.testing.expectEqual(.relative, getWin32PathType(u16, std.unicode.wtf8ToWtf16LeStringLiteral("\u{10000}:\\"))); | ||
| 2686 | } |
lib/std/os/windows.zig+7-128| ... | @@ -2927,7 +2927,7 @@ pub fn CreateSymbolicLink( | ... | @@ -2927,7 +2927,7 @@ pub fn CreateSymbolicLink( |
| 2927 | // Already an NT path, no need to do anything to it | 2927 | // Already an NT path, no need to do anything to it |
| 2928 | break :target_path target_path; | 2928 | break :target_path target_path; |
| 2929 | } else { | 2929 | } else { |
| 2930 | switch (getWin32PathType(u16, target_path)) { | 2930 | switch (std.fs.path.getWin32PathType(u16, target_path)) { |
| 2931 | // Rooted paths need to avoid getting put through wToPrefixedFileW | 2931 | // Rooted paths need to avoid getting put through wToPrefixedFileW |
| 2932 | // (and they are treated as relative in this context) | 2932 | // (and they are treated as relative in this context) |
| 2933 | // Note: It seems that rooted paths in symbolic links are relative to | 2933 | // Note: It seems that rooted paths in symbolic links are relative to |
| ... | @@ -4235,7 +4235,7 @@ pub const RemoveDotDirsError = error{TooManyParentDirs}; | ... | @@ -4235,7 +4235,7 @@ pub const RemoveDotDirsError = error{TooManyParentDirs}; |
| 4235 | /// 2) all repeating back slashes have been collapsed | 4235 | /// 2) all repeating back slashes have been collapsed |
| 4236 | /// 3) the path is a relative one (does not start with a back slash) | 4236 | /// 3) the path is a relative one (does not start with a back slash) |
| 4237 | pub fn removeDotDirsSanitized(comptime T: type, path: []T) RemoveDotDirsError!usize { | 4237 | pub fn removeDotDirsSanitized(comptime T: type, path: []T) RemoveDotDirsError!usize { |
| 4238 | std.debug.assert(path.len == 0 or path[0] != '\\'); | 4238 | assert(path.len == 0 or path[0] != '\\'); |
| 4239 | 4239 | ||
| 4240 | var write_idx: usize = 0; | 4240 | var write_idx: usize = 0; |
| 4241 | var read_idx: usize = 0; | 4241 | var read_idx: usize = 0; |
| ... | @@ -4251,7 +4251,7 @@ pub fn removeDotDirsSanitized(comptime T: type, path: []T) RemoveDotDirsError!us | ... | @@ -4251,7 +4251,7 @@ pub fn removeDotDirsSanitized(comptime T: type, path: []T) RemoveDotDirsError!us |
| 4251 | } | 4251 | } |
| 4252 | if (after_dot == '.' and (read_idx + 2 == path.len or path[read_idx + 2] == '\\')) { | 4252 | if (after_dot == '.' and (read_idx + 2 == path.len or path[read_idx + 2] == '\\')) { |
| 4253 | if (write_idx == 0) return error.TooManyParentDirs; | 4253 | if (write_idx == 0) return error.TooManyParentDirs; |
| 4254 | std.debug.assert(write_idx >= 2); | 4254 | assert(write_idx >= 2); |
| 4255 | write_idx -= 1; | 4255 | write_idx -= 1; |
| 4256 | while (true) { | 4256 | while (true) { |
| 4257 | write_idx -= 1; | 4257 | write_idx -= 1; |
| ... | @@ -4353,7 +4353,7 @@ pub fn wToPrefixedFileW(dir: ?HANDLE, path: [:0]const u16) Wtf16ToPrefixedFileWE | ... | @@ -4353,7 +4353,7 @@ pub fn wToPrefixedFileW(dir: ?HANDLE, path: [:0]const u16) Wtf16ToPrefixedFileWE |
| 4353 | path_space.data[path_space.len] = 0; | 4353 | path_space.data[path_space.len] = 0; |
| 4354 | return path_space; | 4354 | return path_space; |
| 4355 | } else { | 4355 | } else { |
| 4356 | const path_type = getWin32PathType(u16, path); | 4356 | const path_type = std.fs.path.getWin32PathType(u16, path); |
| 4357 | var path_space: PathSpace = undefined; | 4357 | var path_space: PathSpace = undefined; |
| 4358 | if (path_type == .local_device) { | 4358 | if (path_type == .local_device) { |
| 4359 | switch (getLocalDevicePathType(u16, path)) { | 4359 | switch (getLocalDevicePathType(u16, path)) { |
| ... | @@ -4491,8 +4491,8 @@ pub fn wToPrefixedFileW(dir: ?HANDLE, path: [:0]const u16) Wtf16ToPrefixedFileWE | ... | @@ -4491,8 +4491,8 @@ pub fn wToPrefixedFileW(dir: ?HANDLE, path: [:0]const u16) Wtf16ToPrefixedFileWE |
| 4491 | if (path_type == .unc_absolute) { | 4491 | if (path_type == .unc_absolute) { |
| 4492 | // Now add in the UNC, the `C` should overwrite the first `\` of the | 4492 | // Now add in the UNC, the `C` should overwrite the first `\` of the |
| 4493 | // FullPathName, ultimately resulting in `\??\UNC\<the rest of the path>` | 4493 | // FullPathName, ultimately resulting in `\??\UNC\<the rest of the path>` |
| 4494 | std.debug.assert(path_space.data[path_buf_offset] == '\\'); | 4494 | assert(path_space.data[path_buf_offset] == '\\'); |
| 4495 | std.debug.assert(path_space.data[path_buf_offset + 1] == '\\'); | 4495 | assert(path_space.data[path_buf_offset + 1] == '\\'); |
| 4496 | const unc = [_]u16{ 'U', 'N', 'C' }; | 4496 | const unc = [_]u16{ 'U', 'N', 'C' }; |
| 4497 | path_space.data[nt_prefix.len..][0..unc.len].* = unc; | 4497 | path_space.data[nt_prefix.len..][0..unc.len].* = unc; |
| 4498 | } | 4498 | } |
| ... | @@ -4500,127 +4500,6 @@ pub fn wToPrefixedFileW(dir: ?HANDLE, path: [:0]const u16) Wtf16ToPrefixedFileWE | ... | @@ -4500,127 +4500,6 @@ pub fn wToPrefixedFileW(dir: ?HANDLE, path: [:0]const u16) Wtf16ToPrefixedFileWE |
| 4500 | } | 4500 | } |
| 4501 | } | 4501 | } |
| 4502 | 4502 | ||
| 4503 | /// Similar to `RTL_PATH_TYPE`, but without the `UNKNOWN` path type. | ||
| 4504 | pub const Win32PathType = enum { | ||
| 4505 | /// `\\server\share\foo` | ||
| 4506 | unc_absolute, | ||
| 4507 | /// `C:\foo` | ||
| 4508 | drive_absolute, | ||
| 4509 | /// `C:foo` | ||
| 4510 | drive_relative, | ||
| 4511 | /// `\foo` | ||
| 4512 | rooted, | ||
| 4513 | /// `foo` | ||
| 4514 | relative, | ||
| 4515 | /// `\\.\foo`, `\\?\foo` | ||
| 4516 | local_device, | ||
| 4517 | /// `\\.`, `\\?` | ||
| 4518 | root_local_device, | ||
| 4519 | }; | ||
| 4520 | |||
| 4521 | /// Get the path type of a Win32 namespace path. | ||
| 4522 | /// Similar to `RtlDetermineDosPathNameType_U`. | ||
| 4523 | /// If `T` is `u16`, then `path` should be encoded as WTF-16LE. | ||
| 4524 | pub fn getWin32PathType(comptime T: type, path: []const T) Win32PathType { | ||
| 4525 | if (path.len < 1) return .relative; | ||
| 4526 | |||
| 4527 | const windows_path = std.fs.path.PathType.windows; | ||
| 4528 | if (windows_path.isSep(T, path[0])) { | ||
| 4529 | // \x | ||
| 4530 | if (path.len < 2 or !windows_path.isSep(T, path[1])) return .rooted; | ||
| 4531 | // \\. or \\? | ||
| 4532 | if (path.len > 2 and (path[2] == mem.nativeToLittle(T, '.') or path[2] == mem.nativeToLittle(T, '?'))) { | ||
| 4533 | // exactly \\. or \\? with nothing trailing | ||
| 4534 | if (path.len == 3) return .root_local_device; | ||
| 4535 | // \\.\x or \\?\x | ||
| 4536 | if (windows_path.isSep(T, path[3])) return .local_device; | ||
| 4537 | } | ||
| 4538 | // \\x | ||
| 4539 | return .unc_absolute; | ||
| 4540 | } else { | ||
| 4541 | // Some choice has to be made about how non-ASCII code points as drive-letters are handled, since | ||
| 4542 | // path[0] is a different size for WTF-16 vs WTF-8, leading to a potential mismatch in classification | ||
| 4543 | // for a WTF-8 path and its WTF-16 equivalent. For example, `€:\` encoded in WTF-16 is three code | ||
| 4544 | // units `<0x20AC>:\` whereas `€:\` encoded as WTF-8 is 6 code units `<0xE2><0x82><0xAC>:\` so | ||
| 4545 | // checking path[0], path[1] and path[2] would not behave the same between WTF-8/WTF-16. | ||
| 4546 | // | ||
| 4547 | // `RtlDetermineDosPathNameType_U` exclusively deals with WTF-16 and considers | ||
| 4548 | // `€:\` a drive-absolute path, but code points that take two WTF-16 code units to encode get | ||
| 4549 | // classified as a relative path (e.g. with U+20000 as the drive-letter that'd be encoded | ||
| 4550 | // in WTF-16 as `<0xD840><0xDC00>:\` and be considered a relative path). | ||
| 4551 | // | ||
| 4552 | // The choice made here is to emulate the behavior of `RtlDetermineDosPathNameType_U` for both | ||
| 4553 | // WTF-16 and WTF-8. This is because, while unlikely and not supported by the Disk Manager GUI, | ||
| 4554 | // drive letters are not actually restricted to A-Z. Using `SetVolumeMountPointW` will allow you | ||
| 4555 | // to set any byte value as a drive letter, and going through `IOCTL_MOUNTMGR_CREATE_POINT` will | ||
| 4556 | // allow you to set any WTF-16 code unit as a drive letter. | ||
| 4557 | // | ||
| 4558 | // Non-A-Z drive letters don't interact well with most of Windows, but certain things do work, e.g. | ||
| 4559 | // `cd /D €:\` will work, filesystem functions still work, etc. | ||
| 4560 | // | ||
| 4561 | // The unfortunate part of this is that this makes handling WTF-8 more complicated as we can't | ||
| 4562 | // just check path[0], path[1], path[2]. | ||
| 4563 | const colon_i: usize = switch (T) { | ||
| 4564 | u8 => i: { | ||
| 4565 | const code_point_len = std.unicode.utf8ByteSequenceLength(path[0]) catch return .relative; | ||
| 4566 | // Conveniently, 4-byte sequences in WTF-8 have the same starting code point | ||
| 4567 | // as 2-code-unit sequences in WTF-16. | ||
| 4568 | if (code_point_len > 3) return .relative; | ||
| 4569 | break :i code_point_len; | ||
| 4570 | }, | ||
| 4571 | u16 => 1, | ||
| 4572 | else => @compileError("unsupported type: " ++ @typeName(T)), | ||
| 4573 | }; | ||
| 4574 | // x | ||
| 4575 | if (path.len < colon_i + 1 or path[colon_i] != mem.nativeToLittle(T, ':')) return .relative; | ||
| 4576 | // x:\ | ||
| 4577 | if (path.len > colon_i + 1 and windows_path.isSep(T, path[colon_i + 1])) return .drive_absolute; | ||
| 4578 | // x: | ||
| 4579 | return .drive_relative; | ||
| 4580 | } | ||
| 4581 | } | ||
| 4582 | |||
| 4583 | test getWin32PathType { | ||
| 4584 | try std.testing.expectEqual(.relative, getWin32PathType(u8, "")); | ||
| 4585 | try std.testing.expectEqual(.relative, getWin32PathType(u8, "x")); | ||
| 4586 | try std.testing.expectEqual(.relative, getWin32PathType(u8, "x\\")); | ||
| 4587 | |||
| 4588 | try std.testing.expectEqual(.root_local_device, getWin32PathType(u8, "//.")); | ||
| 4589 | try std.testing.expectEqual(.root_local_device, getWin32PathType(u8, "/\\?")); | ||
| 4590 | try std.testing.expectEqual(.root_local_device, getWin32PathType(u8, "\\\\?")); | ||
| 4591 | |||
| 4592 | try std.testing.expectEqual(.local_device, getWin32PathType(u8, "//./x")); | ||
| 4593 | try std.testing.expectEqual(.local_device, getWin32PathType(u8, "/\\?\\x")); | ||
| 4594 | try std.testing.expectEqual(.local_device, getWin32PathType(u8, "\\\\?\\x")); | ||
| 4595 | // local device paths require a path separator after the root, otherwise it is considered a UNC path | ||
| 4596 | try std.testing.expectEqual(.unc_absolute, getWin32PathType(u8, "\\\\?x")); | ||
| 4597 | try std.testing.expectEqual(.unc_absolute, getWin32PathType(u8, "//.x")); | ||
| 4598 | |||
| 4599 | try std.testing.expectEqual(.unc_absolute, getWin32PathType(u8, "//")); | ||
| 4600 | try std.testing.expectEqual(.unc_absolute, getWin32PathType(u8, "\\\\x")); | ||
| 4601 | try std.testing.expectEqual(.unc_absolute, getWin32PathType(u8, "//x")); | ||
| 4602 | |||
| 4603 | try std.testing.expectEqual(.rooted, getWin32PathType(u8, "\\x")); | ||
| 4604 | try std.testing.expectEqual(.rooted, getWin32PathType(u8, "/")); | ||
| 4605 | |||
| 4606 | try std.testing.expectEqual(.drive_relative, getWin32PathType(u8, "x:")); | ||
| 4607 | try std.testing.expectEqual(.drive_relative, getWin32PathType(u8, "x:abc")); | ||
| 4608 | try std.testing.expectEqual(.drive_relative, getWin32PathType(u8, "x:a/b/c")); | ||
| 4609 | |||
| 4610 | try std.testing.expectEqual(.drive_absolute, getWin32PathType(u8, "x:\\")); | ||
| 4611 | try std.testing.expectEqual(.drive_absolute, getWin32PathType(u8, "x:\\abc")); | ||
| 4612 | try std.testing.expectEqual(.drive_absolute, getWin32PathType(u8, "x:/a/b/c")); | ||
| 4613 | |||
| 4614 | // Non-ASCII code point that is encoded as one WTF-16 code unit is considered a valid drive letter | ||
| 4615 | try std.testing.expectEqual(.drive_absolute, getWin32PathType(u8, "€:\\")); | ||
| 4616 | try std.testing.expectEqual(.drive_absolute, getWin32PathType(u16, std.unicode.wtf8ToWtf16LeStringLiteral("€:\\"))); | ||
| 4617 | try std.testing.expectEqual(.drive_relative, getWin32PathType(u8, "€:")); | ||
| 4618 | try std.testing.expectEqual(.drive_relative, getWin32PathType(u16, std.unicode.wtf8ToWtf16LeStringLiteral("€:"))); | ||
| 4619 | // But code points that are encoded as two WTF-16 code units are not | ||
| 4620 | try std.testing.expectEqual(.relative, getWin32PathType(u8, "\u{10000}:\\")); | ||
| 4621 | try std.testing.expectEqual(.relative, getWin32PathType(u16, std.unicode.wtf8ToWtf16LeStringLiteral("\u{10000}:\\"))); | ||
| 4622 | } | ||
| 4623 | |||
| 4624 | /// Returns true if the path starts with `\??\`, which is indicative of an NT path | 4503 | /// Returns true if the path starts with `\??\`, which is indicative of an NT path |
| 4625 | /// but is not enough to fully distinguish between NT paths and Win32 paths, as | 4504 | /// but is not enough to fully distinguish between NT paths and Win32 paths, as |
| 4626 | /// `\??\` is not actually a distinct prefix but rather the path to a special virtual | 4505 | /// `\??\` is not actually a distinct prefix but rather the path to a special virtual |
| ... | @@ -4663,7 +4542,7 @@ const LocalDevicePathType = enum { | ... | @@ -4663,7 +4542,7 @@ const LocalDevicePathType = enum { |
| 4663 | /// Asserts `path` is of type `Win32PathType.local_device`. | 4542 | /// Asserts `path` is of type `Win32PathType.local_device`. |
| 4664 | fn getLocalDevicePathType(comptime T: type, path: []const T) LocalDevicePathType { | 4543 | fn getLocalDevicePathType(comptime T: type, path: []const T) LocalDevicePathType { |
| 4665 | if (std.debug.runtime_safety) { | 4544 | if (std.debug.runtime_safety) { |
| 4666 | std.debug.assert(getWin32PathType(T, path) == .local_device); | 4545 | assert(std.fs.path.getWin32PathType(T, path) == .local_device); |
| 4667 | } | 4546 | } |
| 4668 | 4547 | ||
| 4669 | const backslash = mem.nativeToLittle(T, '\\'); | 4548 | const backslash = mem.nativeToLittle(T, '\\'); |
lib/std/process/Args.zig+2-1| ... | @@ -12,6 +12,7 @@ vector: Vector, | ... | @@ -12,6 +12,7 @@ vector: Vector, |
| 12 | 12 | ||
| 13 | pub const Vector = switch (native_os) { | 13 | pub const Vector = switch (native_os) { |
| 14 | .windows => []const u16, // WTF-16 encoded | 14 | .windows => []const u16, // WTF-16 encoded |
| 15 | .freestanding, .other => void, | ||
| 15 | else => []const [*:0]const u8, | 16 | else => []const [*:0]const u8, |
| 16 | }; | 17 | }; |
| 17 | 18 | ||
| ... | @@ -57,7 +58,7 @@ pub const Iterator = struct { | ... | @@ -57,7 +58,7 @@ pub const Iterator = struct { |
| 57 | /// Returned slice is pointing to the iterator's internal buffer. | 58 | /// Returned slice is pointing to the iterator's internal buffer. |
| 58 | /// On Windows, the result is encoded as [WTF-8](https://wtf-8.codeberg.page/). | 59 | /// On Windows, the result is encoded as [WTF-8](https://wtf-8.codeberg.page/). |
| 59 | /// On other platforms, the result is an opaque sequence of bytes with no particular encoding. | 60 | /// On other platforms, the result is an opaque sequence of bytes with no particular encoding. |
| 60 | pub fn next(it: *Iterator) ?([:0]const u8) { | 61 | pub fn next(it: *Iterator) ?[:0]const u8 { |
| 61 | return it.inner.next(); | 62 | return it.inner.next(); |
| 62 | } | 63 | } |
| 63 | 64 |
lib/std/process/Child.zig+1-1| ... | @@ -21,7 +21,7 @@ pub const Id = switch (native_os) { | ... | @@ -21,7 +21,7 @@ pub const Id = switch (native_os) { |
| 21 | /// On Windows this is the hProcess. | 21 | /// On Windows this is the hProcess. |
| 22 | /// On POSIX this is the pid. | 22 | /// On POSIX this is the pid. |
| 23 | id: ?Id, | 23 | id: ?Id, |
| 24 | thread_handle: if (native_os == .windows) std.os.windows.HANDLE else void = {}, | 24 | thread_handle: if (native_os == .windows) std.os.windows.HANDLE else void, |
| 25 | /// The writing end of the child process's standard input pipe. | 25 | /// The writing end of the child process's standard input pipe. |
| 26 | /// Usage requires `process.SpawnOptions.StdIo.pipe`. | 26 | /// Usage requires `process.SpawnOptions.StdIo.pipe`. |
| 27 | stdin: ?File, | 27 | stdin: ?File, |
lib/std/process/Environ.zig+9-7| ... | @@ -12,11 +12,6 @@ const posix = std.posix; | ... | @@ -12,11 +12,6 @@ const posix = std.posix; |
| 12 | const mem = std.mem; | 12 | const mem = std.mem; |
| 13 | 13 | ||
| 14 | /// Unmodified, unprocessed data provided by the operating system. | 14 | /// Unmodified, unprocessed data provided by the operating system. |
| 15 | /// | ||
| 16 | /// On Windows this might point to memory in the PEB. | ||
| 17 | /// | ||
| 18 | /// On WASI without libc, this is void because the environment has to be | ||
| 19 | /// queried and heap-allocated at runtime. | ||
| 20 | block: Block, | 15 | block: Block, |
| 21 | 16 | ||
| 22 | pub const empty: Environ = .{ | 17 | pub const empty: Environ = .{ |
| ... | @@ -26,12 +21,19 @@ pub const empty: Environ = .{ | ... | @@ -26,12 +21,19 @@ pub const empty: Environ = .{ |
| 26 | }, | 21 | }, |
| 27 | }; | 22 | }; |
| 28 | 23 | ||
| 24 | /// On WASI without libc, this is `void` because the environment has to be | ||
| 25 | /// queried and heap-allocated at runtime. | ||
| 26 | /// | ||
| 27 | /// On Windows, the memory pointed at by the PEB changes when the environment | ||
| 28 | /// is modified, so a long-lived pointer cannot be used. Therefore, on this | ||
| 29 | /// operating system `void` is also used. | ||
| 29 | pub const Block = switch (native_os) { | 30 | pub const Block = switch (native_os) { |
| 30 | .windows => [*:0]const u16, | 31 | .windows => void, |
| 31 | .wasi => switch (builtin.link_libc) { | 32 | .wasi => switch (builtin.link_libc) { |
| 32 | false => void, | 33 | false => void, |
| 33 | true => [:null]const ?[*:0]const u8, | 34 | true => [:null]const ?[*:0]const u8, |
| 34 | }, | 35 | }, |
| 36 | .freestanding, .other => void, | ||
| 35 | else => [:null]const ?[*:0]const u8, | 37 | else => [:null]const ?[*:0]const u8, |
| 36 | }; | 38 | }; |
| 37 | 39 | ||
| ... | @@ -345,7 +347,7 @@ pub fn createMap(env: Environ, allocator: Allocator) CreateMapError!Map { | ... | @@ -345,7 +347,7 @@ pub fn createMap(env: Environ, allocator: Allocator) CreateMapError!Map { |
| 345 | errdefer result.deinit(); | 347 | errdefer result.deinit(); |
| 346 | 348 | ||
| 347 | if (native_os == .windows) { | 349 | if (native_os == .windows) { |
| 348 | const ptr = env.block; | 350 | const ptr = std.os.windows.peb().ProcessParameters.Environment; |
| 349 | 351 | ||
| 350 | var i: usize = 0; | 352 | var i: usize = 0; |
| 351 | while (ptr[i] != 0) { | 353 | while (ptr[i] != 0) { |
lib/std/start.zig+52-109| ... | @@ -11,118 +11,63 @@ const native_os = builtin.os.tag; | ... | @@ -11,118 +11,63 @@ const native_os = builtin.os.tag; |
| 11 | 11 | ||
| 12 | const start_sym_name = if (native_arch.isMIPS()) "__start" else "_start"; | 12 | const start_sym_name = if (native_arch.isMIPS()) "__start" else "_start"; |
| 13 | 13 | ||
| 14 | // The self-hosted compiler is not fully capable of handling all of this start.zig file. | ||
| 15 | // Until then, we have simplified logic here for self-hosted. TODO remove this once | ||
| 16 | // self-hosted is capable enough to handle all of the real start.zig logic. | ||
| 17 | pub const simplified_logic = switch (builtin.zig_backend) { | ||
| 18 | .stage2_aarch64, | ||
| 19 | .stage2_arm, | ||
| 20 | .stage2_powerpc, | ||
| 21 | .stage2_sparc64, | ||
| 22 | .stage2_spirv, | ||
| 23 | .stage2_x86, | ||
| 24 | => true, | ||
| 25 | else => false, | ||
| 26 | }; | ||
| 27 | |||
| 28 | comptime { | 14 | comptime { |
| 29 | // No matter what, we import the root file, so that any export, test, comptime | 15 | // No matter what, we import the root file, so that any export, test, comptime |
| 30 | // decls there get run. | 16 | // decls there get run. |
| 31 | _ = root; | 17 | _ = root; |
| 32 | 18 | ||
| 33 | if (simplified_logic) { | 19 | if (builtin.output_mode == .Lib and builtin.link_mode == .dynamic) { |
| 34 | if (builtin.output_mode == .Exe) { | 20 | if (native_os == .windows and !@hasDecl(root, "_DllMainCRTStartup")) { |
| 35 | if ((builtin.link_libc or builtin.object_format == .c) and @hasDecl(root, "main")) { | 21 | @export(&_DllMainCRTStartup, .{ .name = "_DllMainCRTStartup" }); |
| 36 | if (!@typeInfo(@TypeOf(root.main)).@"fn".calling_convention.eql(.c)) { | ||
| 37 | @export(&main2, .{ .name = "main" }); | ||
| 38 | } | ||
| 39 | } else if (builtin.os.tag == .windows) { | ||
| 40 | if (!@hasDecl(root, "wWinMainCRTStartup") and !@hasDecl(root, "mainCRTStartup")) { | ||
| 41 | @export(&wWinMainCRTStartup2, .{ .name = "wWinMainCRTStartup" }); | ||
| 42 | } | ||
| 43 | } else if (builtin.os.tag == .opencl or builtin.os.tag == .vulkan) { | ||
| 44 | if (@hasDecl(root, "main")) | ||
| 45 | @export(&spirvMain2, .{ .name = "main" }); | ||
| 46 | } else { | ||
| 47 | if (!@hasDecl(root, "_start")) { | ||
| 48 | @export(&_start2, .{ .name = "_start" }); | ||
| 49 | } | ||
| 50 | } | ||
| 51 | } | 22 | } |
| 52 | } else { | 23 | } else if (builtin.output_mode == .Exe or @hasDecl(root, "main")) { |
| 53 | if (builtin.output_mode == .Lib and builtin.link_mode == .dynamic) { | 24 | if (builtin.link_libc and @hasDecl(root, "main")) { |
| 54 | if (native_os == .windows and !@hasDecl(root, "_DllMainCRTStartup")) { | 25 | if (native_arch.isWasm()) { |
| 55 | @export(&_DllMainCRTStartup, .{ .name = "_DllMainCRTStartup" }); | 26 | @export(&mainWithoutEnv, .{ .name = "__main_argc_argv" }); |
| 27 | } else if (!@typeInfo(@TypeOf(root.main)).@"fn".calling_convention.eql(.c)) { | ||
| 28 | @export(&main, .{ .name = "main" }); | ||
| 56 | } | 29 | } |
| 57 | } else if (builtin.output_mode == .Exe or @hasDecl(root, "main")) { | 30 | } else if (native_os == .windows and builtin.link_libc and @hasDecl(root, "wWinMain")) { |
| 58 | if (builtin.link_libc and @hasDecl(root, "main")) { | 31 | if (!@typeInfo(@TypeOf(root.wWinMain)).@"fn".calling_convention.eql(.c)) { |
| 59 | if (native_arch.isWasm()) { | 32 | @export(&wWinMain, .{ .name = "wWinMain" }); |
| 60 | @export(&mainWithoutEnv, .{ .name = "__main_argc_argv" }); | 33 | } |
| 61 | } else if (!@typeInfo(@TypeOf(root.main)).@"fn".calling_convention.eql(.c)) { | 34 | } else if (native_os == .windows) { |
| 62 | @export(&main, .{ .name = "main" }); | 35 | if (!@hasDecl(root, "WinMain") and !@hasDecl(root, "WinMainCRTStartup") and |
| 63 | } | 36 | !@hasDecl(root, "wWinMain") and !@hasDecl(root, "wWinMainCRTStartup")) |
| 64 | } else if (native_os == .windows and builtin.link_libc and @hasDecl(root, "wWinMain")) { | 37 | { |
| 65 | if (!@typeInfo(@TypeOf(root.wWinMain)).@"fn".calling_convention.eql(.c)) { | 38 | @export(&WinStartup, .{ .name = "wWinMainCRTStartup" }); |
| 66 | @export(&wWinMain, .{ .name = "wWinMain" }); | 39 | } else if (@hasDecl(root, "WinMain") and !@hasDecl(root, "WinMainCRTStartup") and |
| 67 | } | 40 | !@hasDecl(root, "wWinMain") and !@hasDecl(root, "wWinMainCRTStartup")) |
| 68 | } else if (native_os == .windows) { | 41 | { |
| 69 | if (!@hasDecl(root, "WinMain") and !@hasDecl(root, "WinMainCRTStartup") and | 42 | @compileError("WinMain not supported; declare wWinMain or main instead"); |
| 70 | !@hasDecl(root, "wWinMain") and !@hasDecl(root, "wWinMainCRTStartup")) | 43 | } else if (@hasDecl(root, "wWinMain") and !@hasDecl(root, "wWinMainCRTStartup") and |
| 71 | { | 44 | !@hasDecl(root, "WinMain") and !@hasDecl(root, "WinMainCRTStartup")) |
| 72 | @export(&WinStartup, .{ .name = "wWinMainCRTStartup" }); | 45 | { |
| 73 | } else if (@hasDecl(root, "WinMain") and !@hasDecl(root, "WinMainCRTStartup") and | 46 | @export(&wWinMainCRTStartup, .{ .name = "wWinMainCRTStartup" }); |
| 74 | !@hasDecl(root, "wWinMain") and !@hasDecl(root, "wWinMainCRTStartup")) | 47 | } |
| 75 | { | 48 | } else if (native_os == .uefi) { |
| 76 | @compileError("WinMain not supported; declare wWinMain or main instead"); | 49 | if (!@hasDecl(root, "EfiMain")) @export(&EfiMain, .{ .name = "EfiMain" }); |
| 77 | } else if (@hasDecl(root, "wWinMain") and !@hasDecl(root, "wWinMainCRTStartup") and | 50 | } else if (native_os == .wasi) { |
| 78 | !@hasDecl(root, "WinMain") and !@hasDecl(root, "WinMainCRTStartup")) | 51 | const wasm_start_sym = switch (builtin.wasi_exec_model) { |
| 79 | { | 52 | .reactor => "_initialize", |
| 80 | @export(&wWinMainCRTStartup, .{ .name = "wWinMainCRTStartup" }); | 53 | .command => "_start", |
| 81 | } | 54 | }; |
| 82 | } else if (native_os == .uefi) { | 55 | if (!@hasDecl(root, wasm_start_sym) and @hasDecl(root, "main")) { |
| 83 | if (!@hasDecl(root, "EfiMain")) @export(&EfiMain, .{ .name = "EfiMain" }); | ||
| 84 | } else if (native_os == .wasi) { | ||
| 85 | const wasm_start_sym = switch (builtin.wasi_exec_model) { | ||
| 86 | .reactor => "_initialize", | ||
| 87 | .command => "_start", | ||
| 88 | }; | ||
| 89 | if (!@hasDecl(root, wasm_start_sym) and @hasDecl(root, "main")) { | ||
| 90 | // Only call main when defined. For WebAssembly it's allowed to pass `-fno-entry` in which | ||
| 91 | // case it's not required to provide an entrypoint such as main. | ||
| 92 | @export(&wasi_start, .{ .name = wasm_start_sym }); | ||
| 93 | } | ||
| 94 | } else if (native_arch.isWasm() and native_os == .freestanding) { | ||
| 95 | // Only call main when defined. For WebAssembly it's allowed to pass `-fno-entry` in which | 56 | // Only call main when defined. For WebAssembly it's allowed to pass `-fno-entry` in which |
| 96 | // case it's not required to provide an entrypoint such as main. | 57 | // case it's not required to provide an entrypoint such as main. |
| 97 | if (!@hasDecl(root, start_sym_name) and @hasDecl(root, "main")) @export(&wasm_freestanding_start, .{ .name = start_sym_name }); | 58 | @export(&wasi_start, .{ .name = wasm_start_sym }); |
| 98 | } else switch (native_os) { | ||
| 99 | .other, .freestanding, .@"3ds", .vita => {}, | ||
| 100 | else => if (!@hasDecl(root, start_sym_name)) @export(&_start, .{ .name = start_sym_name }), | ||
| 101 | } | 59 | } |
| 60 | } else if (native_arch.isWasm() and native_os == .freestanding) { | ||
| 61 | // Only call main when defined. For WebAssembly it's allowed to pass `-fno-entry` in which | ||
| 62 | // case it's not required to provide an entrypoint such as main. | ||
| 63 | if (!@hasDecl(root, start_sym_name) and @hasDecl(root, "main")) @export(&wasm_freestanding_start, .{ .name = start_sym_name }); | ||
| 64 | } else switch (native_os) { | ||
| 65 | .other, .freestanding, .@"3ds", .vita => {}, | ||
| 66 | else => if (!@hasDecl(root, start_sym_name)) @export(&_start, .{ .name = start_sym_name }), | ||
| 102 | } | 67 | } |
| 103 | } | 68 | } |
| 104 | } | 69 | } |
| 105 | 70 | ||
| 106 | // Simplified start code for stage2 until it supports more language features /// | ||
| 107 | |||
| 108 | fn main2() callconv(.c) c_int { | ||
| 109 | return callMain(); | ||
| 110 | } | ||
| 111 | |||
| 112 | fn _start2() callconv(.withStackAlign(.c, 1)) noreturn { | ||
| 113 | std.process.exit(callMain()); | ||
| 114 | } | ||
| 115 | |||
| 116 | fn spirvMain2() callconv(.kernel) void { | ||
| 117 | root.main(); | ||
| 118 | } | ||
| 119 | |||
| 120 | fn wWinMainCRTStartup2() callconv(.c) noreturn { | ||
| 121 | std.process.exit(callMain()); | ||
| 122 | } | ||
| 123 | |||
| 124 | //////////////////////////////////////////////////////////////////////////////// | ||
| 125 | |||
| 126 | fn _DllMainCRTStartup( | 71 | fn _DllMainCRTStartup( |
| 127 | hinstDLL: std.os.windows.HINSTANCE, | 72 | hinstDLL: std.os.windows.HINSTANCE, |
| 128 | fdwReason: std.os.windows.DWORD, | 73 | fdwReason: std.os.windows.DWORD, |
| ... | @@ -142,15 +87,15 @@ fn _DllMainCRTStartup( | ... | @@ -142,15 +87,15 @@ fn _DllMainCRTStartup( |
| 142 | fn wasm_freestanding_start() callconv(.c) void { | 87 | fn wasm_freestanding_start() callconv(.c) void { |
| 143 | // This is marked inline because for some reason LLVM in | 88 | // This is marked inline because for some reason LLVM in |
| 144 | // release mode fails to inline it, and we want fewer call frames in stack traces. | 89 | // release mode fails to inline it, and we want fewer call frames in stack traces. |
| 145 | _ = @call(.always_inline, callMain, .{}); | 90 | _ = @call(.always_inline, callMain, .{ {}, {} }); |
| 146 | } | 91 | } |
| 147 | 92 | ||
| 148 | fn wasi_start() callconv(.c) void { | 93 | fn wasi_start() callconv(.c) void { |
| 149 | // The function call is marked inline because for some reason LLVM in | 94 | // The function call is marked inline because for some reason LLVM in |
| 150 | // release mode fails to inline it, and we want fewer call frames in stack traces. | 95 | // release mode fails to inline it, and we want fewer call frames in stack traces. |
| 151 | switch (builtin.wasi_exec_model) { | 96 | switch (builtin.wasi_exec_model) { |
| 152 | .reactor => _ = @call(.always_inline, callMain, .{}), | 97 | .reactor => _ = @call(.always_inline, callMain, .{ {}, {} }), |
| 153 | .command => std.os.wasi.proc_exit(@call(.always_inline, callMain, .{})), | 98 | .command => std.os.wasi.proc_exit(@call(.always_inline, callMain, .{ {}, {} })), |
| 154 | } | 99 | } |
| 155 | } | 100 | } |
| 156 | 101 | ||
| ... | @@ -524,13 +469,10 @@ fn WinStartup() callconv(.withStackAlign(.c, 1)) noreturn { | ... | @@ -524,13 +469,10 @@ fn WinStartup() callconv(.withStackAlign(.c, 1)) noreturn { |
| 524 | 469 | ||
| 525 | std.debug.maybeEnableSegfaultHandler(); | 470 | std.debug.maybeEnableSegfaultHandler(); |
| 526 | 471 | ||
| 527 | const peb = std.os.windows.peb(); | ||
| 528 | const cmd_line = std.os.windows.peb().ProcessParameters.CommandLine; | 472 | const cmd_line = std.os.windows.peb().ProcessParameters.CommandLine; |
| 473 | const cmd_line_w = cmd_line.Buffer.?[0..@divExact(cmd_line.Length, 2)]; | ||
| 529 | 474 | ||
| 530 | std.os.windows.ntdll.RtlExitUserProcess(callMain( | 475 | std.os.windows.ntdll.RtlExitUserProcess(callMain(cmd_line_w, {})); |
| 531 | cmd_line.Buffer.?[0..@divExact(cmd_line.Length, 2)], | ||
| 532 | peb.ProcessParameters.Environment, | ||
| 533 | )); | ||
| 534 | } | 476 | } |
| 535 | 477 | ||
| 536 | fn wWinMainCRTStartup() callconv(.withStackAlign(.c, 1)) noreturn { | 478 | fn wWinMainCRTStartup() callconv(.withStackAlign(.c, 1)) noreturn { |
| ... | @@ -637,6 +579,7 @@ fn posixCallMainAndExit(argc_argv_ptr: [*]usize) callconv(.c) noreturn { | ... | @@ -637,6 +579,7 @@ fn posixCallMainAndExit(argc_argv_ptr: [*]usize) callconv(.c) noreturn { |
| 637 | } | 579 | } |
| 638 | 580 | ||
| 639 | fn expandStackSize(phdrs: []elf.Phdr) void { | 581 | fn expandStackSize(phdrs: []elf.Phdr) void { |
| 582 | @disableInstrumentation(); | ||
| 640 | for (phdrs) |*phdr| { | 583 | for (phdrs) |*phdr| { |
| 641 | switch (phdr.p_type) { | 584 | switch (phdr.p_type) { |
| 642 | elf.PT_GNU_STACK => { | 585 | elf.PT_GNU_STACK => { |
| ... | @@ -674,7 +617,7 @@ fn expandStackSize(phdrs: []elf.Phdr) void { | ... | @@ -674,7 +617,7 @@ fn expandStackSize(phdrs: []elf.Phdr) void { |
| 674 | inline fn callMainWithArgs(argc: usize, argv: [*][*:0]u8, envp: [:null]?[*:0]u8) u8 { | 617 | inline fn callMainWithArgs(argc: usize, argv: [*][*:0]u8, envp: [:null]?[*:0]u8) u8 { |
| 675 | if (std.Options.debug_threaded_io) |t| { | 618 | if (std.Options.debug_threaded_io) |t| { |
| 676 | if (@sizeOf(std.Io.Threaded.Argv0) != 0) t.argv0.value = argv[0]; | 619 | if (@sizeOf(std.Io.Threaded.Argv0) != 0) t.argv0.value = argv[0]; |
| 677 | t.environ = .{ .block = envp }; | 620 | t.environ = .{ .process_environ = .{ .block = envp } }; |
| 678 | } | 621 | } |
| 679 | std.debug.maybeEnableSegfaultHandler(); | 622 | std.debug.maybeEnableSegfaultHandler(); |
| 680 | return callMain(argv[0..argc], envp); | 623 | return callMain(argv[0..argc], envp); |
| ... | @@ -735,8 +678,8 @@ inline fn callMain(args: std.process.Args.Vector, environ: std.process.Environ.B | ... | @@ -735,8 +678,8 @@ inline fn callMain(args: std.process.Args.Vector, environ: std.process.Environ.B |
| 735 | defer arena_allocator.deinit(); | 678 | defer arena_allocator.deinit(); |
| 736 | 679 | ||
| 737 | var threaded: std.Io.Threaded = .init(gpa, .{ | 680 | var threaded: std.Io.Threaded = .init(gpa, .{ |
| 738 | .argv0 = if (@sizeOf(std.Io.Threaded.Argv0) != 0) .{ .value = args[0] } else .{}, | 681 | .argv0 = .init(.{ .value = args }), |
| 739 | .environ = .{ .block = environ }, | 682 | .environ = .{ .process_environ = .{ .block = environ } }, |
| 740 | }); | 683 | }); |
| 741 | defer threaded.deinit(); | 684 | defer threaded.deinit(); |
| 742 | 685 |