| 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 | 84 | .io = io, |
| 85 | 85 | .gpa = arena, |
| 86 | 86 | .manifest_dir = try local_cache_directory.handle.createDirPathOpen(io, "h", .{}), |
| 87 | .cwd = try process.getCwdAlloc(single_threaded_arena.allocator()), | |
| 87 | 88 | }, |
| 88 | 89 | .zig_exe = zig_exe, |
| 89 | 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 | 38 | } |
| 39 | 39 | |
| 40 | 40 | if (need_simple) { |
| 41 | return mainSimple() catch @panic("test failure\n"); | |
| 41 | return mainSimple() catch @panic("test failure"); | |
| 42 | 42 | } |
| 43 | 43 | |
| 44 | 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 | 1738 | } |
| 1739 | 1739 | |
| 1740 | 1740 | fn pathFromCwd(b: *Build, sub_path: []const u8) []u8 { |
| 1741 | const cwd = process.getCwdAlloc(b.allocator) catch @panic("OOM"); | |
| 1742 | return b.pathResolve(&.{ cwd, sub_path }); | |
| 1741 | return b.pathResolve(&.{ b.graph.cache.cwd, sub_path }); | |
| 1743 | 1742 | } |
| 1744 | 1743 | |
| 1745 | 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 | 30 | /// and usefulness of the cache for advanced use cases. |
| 31 | 31 | prefixes_buffer: [4]Directory = undefined, |
| 32 | 32 | prefixes_len: usize = 0, |
| 33 | /// Used to identify prefixes. References external memory. | |
| 34 | cwd: []const u8, | |
| 33 | 35 | |
| 34 | 36 | pub const Path = @import("Cache/Path.zig"); |
| 35 | 37 | pub const Directory = @import("Cache/Directory.zig"); |
| ... | ... | @@ -78,11 +80,12 @@ fn findPrefix(cache: *const Cache, file_path: []const u8) !PrefixedPath { |
| 78 | 80 | /// Takes ownership of `resolved_path` on success. |
| 79 | 81 | fn findPrefixResolved(cache: *const Cache, resolved_path: []u8) !PrefixedPath { |
| 80 | 82 | const gpa = cache.gpa; |
| 83 | const cwd = cache.cwd; | |
| 81 | 84 | const prefixes_slice = cache.prefixes(); |
| 82 | 85 | var i: u8 = 1; // Start at 1 to skip over checking the null prefix. |
| 83 | 86 | while (i < prefixes_slice.len) : (i += 1) { |
| 84 | 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 | 89 | error.NotASubPath => continue, |
| 87 | 90 | else => |e| return e, |
| 88 | 91 | }; |
| ... | ... | @@ -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 { | |
| 104 | const relative = try std.fs.path.relative(allocator, prefix, path); | |
| 105 | errdefer allocator.free(relative); | |
| 106 | var component_iterator = std.fs.path.NativeComponentIterator.init(relative); | |
| 106 | fn getPrefixSubpath(gpa: Allocator, cwd: []const u8, prefix: []const u8, path: []u8) ![]u8 { | |
| 107 | const relative = try std.fs.path.relative(gpa, cwd, null, prefix, path); | |
| 108 | errdefer gpa.free(relative); | |
| 109 | var component_iterator: std.fs.path.NativeComponentIterator = .init(relative); | |
| 107 | 110 | if (component_iterator.root() != null) { |
| 108 | 111 | return error.NotASubPath; |
| 109 | 112 | } |
| ... | ... | @@ -1307,11 +1310,14 @@ fn testGetCurrentFileTimestamp(io: Io, dir: Io.Dir) !Io.Timestamp { |
| 1307 | 1310 | } |
| 1308 | 1311 | |
| 1309 | 1312 | test "cache file and then recall it" { |
| 1310 | const io = std.testing.io; | |
| 1313 | const io = testing.io; | |
| 1311 | 1314 | |
| 1312 | 1315 | var tmp = testing.tmpDir(.{}); |
| 1313 | 1316 | defer tmp.cleanup(); |
| 1314 | 1317 | |
| 1318 | const cwd = try std.process.getCwdAlloc(testing.allocator); | |
| 1319 | defer testing.allocator.free(cwd); | |
| 1320 | ||
| 1315 | 1321 | const temp_file = "test.txt"; |
| 1316 | 1322 | const temp_manifest_dir = "temp_manifest_dir"; |
| 1317 | 1323 | |
| ... | ... | @@ -1331,6 +1337,7 @@ test "cache file and then recall it" { |
| 1331 | 1337 | .io = io, |
| 1332 | 1338 | .gpa = testing.allocator, |
| 1333 | 1339 | .manifest_dir = try tmp.dir.createDirPathOpen(io, temp_manifest_dir, .{}), |
| 1340 | .cwd = cwd, | |
| 1334 | 1341 | }; |
| 1335 | 1342 | cache.addPrefix(.{ .path = null, .handle = tmp.dir }); |
| 1336 | 1343 | defer cache.manifest_dir.close(io); |
| ... | ... | @@ -1371,11 +1378,14 @@ test "cache file and then recall it" { |
| 1371 | 1378 | } |
| 1372 | 1379 | |
| 1373 | 1380 | test "check that changing a file makes cache fail" { |
| 1374 | const io = std.testing.io; | |
| 1381 | const io = testing.io; | |
| 1375 | 1382 | |
| 1376 | 1383 | var tmp = testing.tmpDir(.{}); |
| 1377 | 1384 | defer tmp.cleanup(); |
| 1378 | 1385 | |
| 1386 | const cwd = try std.process.getCwdAlloc(testing.allocator); | |
| 1387 | defer testing.allocator.free(cwd); | |
| 1388 | ||
| 1379 | 1389 | const temp_file = "cache_hash_change_file_test.txt"; |
| 1380 | 1390 | const temp_manifest_dir = "cache_hash_change_file_manifest_dir"; |
| 1381 | 1391 | const original_temp_file_contents = "Hello, world!\n"; |
| ... | ... | @@ -1397,6 +1407,7 @@ test "check that changing a file makes cache fail" { |
| 1397 | 1407 | .io = io, |
| 1398 | 1408 | .gpa = testing.allocator, |
| 1399 | 1409 | .manifest_dir = try tmp.dir.createDirPathOpen(io, temp_manifest_dir, .{}), |
| 1410 | .cwd = cwd, | |
| 1400 | 1411 | }; |
| 1401 | 1412 | cache.addPrefix(.{ .path = null, .handle = tmp.dir }); |
| 1402 | 1413 | defer cache.manifest_dir.close(io); |
| ... | ... | @@ -1448,6 +1459,9 @@ test "no file inputs" { |
| 1448 | 1459 | var tmp = testing.tmpDir(.{}); |
| 1449 | 1460 | defer tmp.cleanup(); |
| 1450 | 1461 | |
| 1462 | const cwd = try std.process.getCwdAlloc(testing.allocator); | |
| 1463 | defer testing.allocator.free(cwd); | |
| 1464 | ||
| 1451 | 1465 | const temp_manifest_dir = "no_file_inputs_manifest_dir"; |
| 1452 | 1466 | |
| 1453 | 1467 | var digest1: HexDigest = undefined; |
| ... | ... | @@ -1457,6 +1471,7 @@ test "no file inputs" { |
| 1457 | 1471 | .io = io, |
| 1458 | 1472 | .gpa = testing.allocator, |
| 1459 | 1473 | .manifest_dir = try tmp.dir.createDirPathOpen(io, temp_manifest_dir, .{}), |
| 1474 | .cwd = cwd, | |
| 1460 | 1475 | }; |
| 1461 | 1476 | cache.addPrefix(.{ .path = null, .handle = tmp.dir }); |
| 1462 | 1477 | defer cache.manifest_dir.close(io); |
| ... | ... | @@ -1489,11 +1504,14 @@ test "no file inputs" { |
| 1489 | 1504 | } |
| 1490 | 1505 | |
| 1491 | 1506 | test "Manifest with files added after initial hash work" { |
| 1492 | const io = std.testing.io; | |
| 1507 | const io = testing.io; | |
| 1493 | 1508 | |
| 1494 | 1509 | var tmp = testing.tmpDir(.{}); |
| 1495 | 1510 | defer tmp.cleanup(); |
| 1496 | 1511 | |
| 1512 | const cwd = try std.process.getCwdAlloc(testing.allocator); | |
| 1513 | defer testing.allocator.free(cwd); | |
| 1514 | ||
| 1497 | 1515 | const temp_file1 = "cache_hash_post_file_test1.txt"; |
| 1498 | 1516 | const temp_file2 = "cache_hash_post_file_test2.txt"; |
| 1499 | 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 | 1534 | .io = io, |
| 1517 | 1535 | .gpa = testing.allocator, |
| 1518 | 1536 | .manifest_dir = try tmp.dir.createDirPathOpen(io, temp_manifest_dir, .{}), |
| 1537 | .cwd = cwd, | |
| 1519 | 1538 | }; |
| 1520 | 1539 | cache.addPrefix(.{ .path = null, .handle = tmp.dir }); |
| 1521 | 1540 | defer cache.manifest_dir.close(io); |
lib/std/Build/Step/Options.zig+4| ... | ... | @@ -537,6 +537,9 @@ test Options { |
| 537 | 537 | var arena = std.heap.ArenaAllocator.init(std.testing.allocator); |
| 538 | 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 | 543 | var graph: std.Build.Graph = .{ |
| 541 | 544 | .io = io, |
| 542 | 545 | .arena = arena.allocator(), |
| ... | ... | @@ -544,6 +547,7 @@ test Options { |
| 544 | 547 | .io = io, |
| 545 | 548 | .gpa = arena.allocator(), |
| 546 | 549 | .manifest_dir = Io.Dir.cwd(), |
| 550 | .cwd = cwd, | |
| 547 | 551 | }, |
| 548 | 552 | .zig_exe = "test", |
| 549 | 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 | 750 | /// to make sure the child doesn't see paths relative to a cwd other than its own. |
| 751 | 751 | fn convertPathArg(run: *Run, path: Build.Cache.Path) []const u8 { |
| 752 | 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 | 757 | if (Dir.path.isAbsolute(path_str)) { |
| 755 | 758 | // Absolute paths don't need changing. |
| 756 | 759 | return path_str; |
| 757 | 760 | } |
| 758 | 761 | const child_cwd_rel: []const u8 = rel: { |
| 759 | 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 | 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 | 767 | // Not every path can be made relative, e.g. if the path and the child cwd are on different |
| 765 | 768 | // disk designators on Windows. In that case, `relative` will return an absolute path which we can |
| 766 | 769 | // just return. |
| 767 | if (Dir.path.isAbsolute(child_cwd_rel)) { | |
| 768 | return child_cwd_rel; | |
| 769 | } | |
| 770 | if (Dir.path.isAbsolute(child_cwd_rel)) return child_cwd_rel; | |
| 771 | ||
| 770 | 772 | // We're not done yet. In some cases this path must be prefixed with './': |
| 771 | 773 | // * On POSIX, the executable name cannot be a single component like 'foo' |
| 772 | 774 | // * Some executables might treat a leading '-' like a flag, which we must avoid |
| 773 | 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 | 779 | const IndexedOutput = struct { |
lib/std/Build/Watch/FsEvents.zig+7-5| ... | ... | @@ -43,6 +43,8 @@ dispatch_queue: dispatch_queue_t, |
| 43 | 43 | /// of writing. See the comment at the start of `wait` for details. |
| 44 | 44 | since_event: FSEventStreamEventId, |
| 45 | 45 | |
| 46 | cwd_path: []const u8, | |
| 47 | ||
| 46 | 48 | /// All of the symbols we pull from the `dlopen`ed CoreServices framework. If any of these symbols |
| 47 | 49 | /// is not present, `init` will close the framework and return an error. |
| 48 | 50 | const ResolvedSymbols = struct { |
| ... | ... | @@ -78,7 +80,7 @@ const ResolvedSymbols = struct { |
| 78 | 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 | 84 | var core_services = std.DynLib.open("/System/Library/Frameworks/CoreServices.framework/CoreServices") catch |
| 83 | 85 | return error.OpenFrameworkFailed; |
| 84 | 86 | errdefer core_services.close(); |
| ... | ... | @@ -99,6 +101,7 @@ pub fn init() error{ OpenFrameworkFailed, MissingCoreServicesSymbol }!FsEvents { |
| 99 | 101 | // Not `.since_now`, because this means we can init `FsEvents` *before* we do work in order |
| 100 | 102 | // to notice any changes which happened during said work. |
| 101 | 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 | 123 | defer fse.paths_arena = paths_arena_instance.state; |
| 121 | 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 | 126 | var need_dirs: std.StringArrayHashMapUnmanaged(void) = .empty; |
| 127 | 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 | 131 | // We take `step` by pointer for a slight memory optimization in a moment. |
| 132 | 132 | for (steps) |*step| { |
| 133 | 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 | 137 | try need_dirs.put(gpa, resolved_dir, {}); |
| 136 | 138 | for (files.items) |file_name| { |
| 137 | 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 | 482 | }); |
| 483 | 483 | } |
| 484 | 484 | pub fn serveTarFile(ws: *WebServer, request: *http.Server.Request, paths: []const Cache.Path) !void { |
| 485 | const gpa = ws.gpa; | |
| 486 | const io = ws.graph.io; | |
| 485 | const graph = ws.graph; | |
| 486 | const io = graph.io; | |
| 487 | 487 | |
| 488 | 488 | var send_buffer: [0x4000]u8 = undefined; |
| 489 | 489 | var response = try request.respondStreaming(&send_buffer, .{ |
| ... | ... | @@ -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 | 498 | var archiver: std.tar.Writer = .{ .underlying_writer = &response.writer }; |
| 502 | 499 | |
| 503 | 500 | for (paths) |path| { |
| ... | ... | @@ -516,10 +513,7 @@ pub fn serveTarFile(ws: *WebServer, request: *http.Server.Request, paths: []cons |
| 516 | 513 | // resulting in modules named "" and "src". The compiler needs to tell the build system |
| 517 | 514 | // about the module graph so that the build system can correctly encode this information in |
| 518 | 515 | // the tar file. |
| 519 | archiver.prefix = path.root_dir.path orelse cwd: { | |
| 520 | if (cached_cwd_path == null) cached_cwd_path = try std.process.getCwdAlloc(gpa); | |
| 521 | break :cwd cached_cwd_path.?; | |
| 522 | }; | |
| 516 | archiver.prefix = path.root_dir.path orelse graph.cache.cwd; | |
| 523 | 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 | 86 | |
| 87 | 87 | const Environ = struct { |
| 88 | 88 | /// Unmodified data directly from the OS. |
| 89 | block: process.Environ.Block = &.{}, | |
| 89 | process_environ: process.Environ = .empty, | |
| 90 | 90 | /// Protected by `mutex`. Determines whether the other fields have been |
| 91 | /// memoized based on `block`. | |
| 91 | /// memoized based on `process_environ`. | |
| 92 | 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 | 94 | /// environment variables are present, ignoring their value. |
| 95 | 95 | exist: Exist = .{}, |
| 96 | /// Protected by `mutex`. Memoized based on `block`. | |
| 96 | /// Protected by `mutex`. Memoized based on `process_environ`. | |
| 97 | 97 | string: String = .{}, |
| 98 | 98 | /// ZIG_PROGRESS |
| 99 | 99 | zig_progress_handle: std.Progress.ParentFileError!u31 = error.EnvironmentVariableMissing, |
| ... | ... | @@ -1186,7 +1186,7 @@ pub fn init( |
| 1186 | 1186 | .have_signal_handler = false, |
| 1187 | 1187 | .argv0 = options.argv0, |
| 1188 | 1188 | .worker_threads = .init(null), |
| 1189 | .environ = .{ .block = options.environ.block }, | |
| 1189 | .environ = .{ .process_environ = options.environ }, | |
| 1190 | 1190 | .robust_cancel = options.robust_cancel, |
| 1191 | 1191 | }; |
| 1192 | 1192 | |
| ... | ... | @@ -12693,7 +12693,7 @@ fn scanEnviron(t: *Threaded) void { |
| 12693 | 12693 | comptime assert(@sizeOf(Environ.String) == 0); |
| 12694 | 12694 | } |
| 12695 | 12695 | } else { |
| 12696 | for (t.environ.block) |opt_line| { | |
| 12696 | for (t.environ.process_environ.block) |opt_line| { | |
| 12697 | 12697 | const line = opt_line.?; |
| 12698 | 12698 | var line_i: usize = 0; |
| 12699 | 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 | 12837 | .zig_progress_fd = prog_fd, |
| 12838 | 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 | 12841 | .zig_progress_fd = prog_fd, |
| 12842 | 12842 | })).ptr; |
| 12843 | 12843 | }; |
| ... | ... | @@ -12934,6 +12934,7 @@ fn processSpawnPosix(userdata: ?*anyopaque, options: process.SpawnOptions) proce |
| 12934 | 12934 | |
| 12935 | 12935 | return .{ |
| 12936 | 12936 | .id = pid, |
| 12937 | .thread_handle = {}, | |
| 12937 | 12938 | .stdin = switch (options.stdin) { |
| 12938 | 12939 | .pipe => .{ .handle = stdin_pipe[1] }, |
| 12939 | 12940 | else => null, |
lib/std/fs/path.zig+277-147| ... | ... | @@ -13,16 +13,15 @@ |
| 13 | 13 | //! https://github.com/WebAssembly/wasi-filesystem/issues/17#issuecomment-1430639353 |
| 14 | 14 | |
| 15 | 15 | const builtin = @import("builtin"); |
| 16 | const native_os = builtin.target.os.tag; | |
| 17 | ||
| 16 | 18 | const std = @import("../std.zig"); |
| 17 | const debug = std.debug; | |
| 18 | const assert = debug.assert; | |
| 19 | const assert = std.debug.assert; | |
| 19 | 20 | const testing = std.testing; |
| 20 | 21 | const mem = std.mem; |
| 21 | const ascii = std.ascii; | |
| 22 | const Allocator = mem.Allocator; | |
| 23 | const windows = std.os.windows; | |
| 24 | const process = std.process; | |
| 25 | const native_os = builtin.target.os.tag; | |
| 22 | const Allocator = std.mem.Allocator; | |
| 23 | const eqlIgnoreCaseWtf8 = std.os.windows.eqlIgnoreCaseWtf8; | |
| 24 | const eqlIgnoreCaseWtf16 = std.os.windows.eqlIgnoreCaseWtf16; | |
| 26 | 25 | |
| 27 | 26 | pub const sep_windows: u8 = '\\'; |
| 28 | 27 | pub const sep_posix: u8 = '/'; |
| ... | ... | @@ -281,7 +280,7 @@ pub fn isAbsolute(path: []const u8) bool { |
| 281 | 280 | } |
| 282 | 281 | |
| 283 | 282 | fn isAbsoluteWindowsImpl(comptime T: type, path: []const T) bool { |
| 284 | return switch (windows.getWin32PathType(T, path)) { | |
| 283 | return switch (getWin32PathType(T, path)) { | |
| 285 | 284 | // Unambiguously absolute |
| 286 | 285 | .drive_absolute, .unc_absolute, .local_device, .root_local_device => true, |
| 287 | 286 | // Unambiguously relative |
| ... | ... | @@ -515,13 +514,13 @@ test parsePathPosix { |
| 515 | 514 | |
| 516 | 515 | pub fn WindowsPath2(comptime T: type) type { |
| 517 | 516 | return struct { |
| 518 | kind: windows.Win32PathType, | |
| 517 | kind: Win32PathType, | |
| 519 | 518 | root: []const T, |
| 520 | 519 | }; |
| 521 | 520 | } |
| 522 | 521 | |
| 523 | 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 | 524 | const root = root: switch (kind) { |
| 526 | 525 | .drive_absolute, .drive_relative => { |
| 527 | 526 | const drive_letter_len = getDriveLetter(T, path).len; |
| ... | ... | @@ -731,7 +730,7 @@ fn parseUNC(comptime T: type, path: []const T) WindowsUNC(T) { |
| 731 | 730 | // For the share, there can be any number of path separators between the server |
| 732 | 731 | // and the share, so we want to skip over all of them instead of just looking for |
| 733 | 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 | 734 | const share = it.next() orelse return .{ |
| 736 | 735 | .server = path[2..server_end], |
| 737 | 736 | .sep_after_server = true, |
| ... | ... | @@ -803,8 +802,8 @@ const DiskDesignatorKind = enum { drive, unc }; |
| 803 | 802 | /// `p1` and `p2` are both assumed to be the `kind` provided. |
| 804 | 803 | fn compareDiskDesignators(comptime T: type, kind: DiskDesignatorKind, p1: []const T, p2: []const T) bool { |
| 805 | 804 | const eql = switch (T) { |
| 806 | u8 => windows.eqlIgnoreCaseWtf8, | |
| 807 | u16 => windows.eqlIgnoreCaseWtf16, | |
| 805 | u8 => eqlIgnoreCaseWtf8, | |
| 806 | u16 => eqlIgnoreCaseWtf16, | |
| 808 | 807 | else => @compileError("only u8 (WTF-8) and u16 (WTF-16LE) is supported"), |
| 809 | 808 | }; |
| 810 | 809 | switch (kind) { |
| ... | ... | @@ -1094,10 +1093,14 @@ pub fn resolveWindows(allocator: Allocator, paths: []const []const u8) Allocator |
| 1094 | 1093 | } |
| 1095 | 1094 | |
| 1096 | 1095 | /// This function is like a series of `cd` statements executed one after another. |
| 1096 | /// | |
| 1097 | 1097 | /// It resolves "." and ".." to the best of its ability, but will not convert relative paths to |
| 1098 | 1098 | /// an absolute path, use Io.Dir.realpath instead. |
| 1099 | /// | |
| 1099 | 1100 | /// ".." components may persist in the resolved path if the resolved path is relative. |
| 1101 | /// | |
| 1100 | 1102 | /// The result does not have a trailing path separator. |
| 1103 | /// | |
| 1101 | 1104 | /// This function does not perform any syscalls. Executing this series of path |
| 1102 | 1105 | /// lookups on the actual filesystem may produce different results due to |
| 1103 | 1106 | /// symlinks. |
| ... | ... | @@ -1494,25 +1497,54 @@ fn testBasenameWindows(input: []const u8, expected_output: []const u8) !void { |
| 1494 | 1497 | try testing.expectEqualSlices(u8, expected_output, basenameWindows(input)); |
| 1495 | 1498 | } |
| 1496 | 1499 | |
| 1497 | pub const RelativeError = std.process.GetCwdAllocError; | |
| 1498 | ||
| 1499 | /// Returns the relative path from `from` to `to`. If `from` and `to` each | |
| 1500 | /// resolve to the same path (after calling `resolve` on each), a zero-length | |
| 1501 | /// string is returned. | |
| 1502 | /// On Windows, the result is not guaranteed to be relative, as the paths may be | |
| 1503 | /// on different volumes. In that case, the result will be the canonicalized absolute | |
| 1504 | /// path of `to`. | |
| 1505 | pub fn relative(allocator: Allocator, from: []const u8, to: []const u8) RelativeError![]u8 { | |
| 1500 | /// Returns the non-absolute path from `from` to `to`. | |
| 1501 | /// | |
| 1502 | /// Other than memory allocation, this is a pure function; the result solely | |
| 1503 | /// depends on the input parameters. | |
| 1504 | /// | |
| 1505 | /// If `from` and `to` each resolve to the same path (after calling `resolve` | |
| 1506 | /// on each), a zero-length string is returned. | |
| 1507 | /// | |
| 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 | 1517 | if (native_os == .windows) { |
| 1507 | return relativeWindows(allocator, from, to); | |
| 1518 | return relativeWindows(gpa, cwd, env_map, from, to); | |
| 1508 | 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 { | |
| 1514 | if (native_os != .windows) @compileError("this function relies on Windows-specific semantics"); | |
| 1515 | ||
| 1524 | /// Returns the non-absolute path from `from` to `to` according to Windows rules. | |
| 1525 | /// | |
| 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 | 1548 | const parsed_from = parsePathWindows(u8, from); |
| 1517 | 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 | 1565 | }; |
| 1534 | 1566 | |
| 1535 | 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); | |
| 1540 | defer allocator.free(resolved_from); | |
| 1571 | const resolved_from = try windowsResolveAgainstCwd(gpa, cwd, env_map, from, parsed_from); | |
| 1572 | defer gpa.free(resolved_from); | |
| 1541 | 1573 | var clean_up_resolved_to = true; |
| 1542 | const resolved_to = try windowsResolveAgainstCwd(allocator, to, parsed_to); | |
| 1543 | defer if (clean_up_resolved_to) allocator.free(resolved_to); | |
| 1574 | const resolved_to = try windowsResolveAgainstCwd(gpa, cwd, env_map, to, parsed_to); | |
| 1575 | defer if (clean_up_resolved_to) gpa.free(resolved_to); | |
| 1544 | 1576 | |
| 1545 | 1577 | const parsed_resolved_from = parsePathWindows(u8, resolved_from); |
| 1546 | 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 | 1601 | var from_it = mem.tokenizeAny(u8, resolved_from[parsed_resolved_from.root.len..], "/\\"); |
| 1570 | 1602 | var to_it = mem.tokenizeAny(u8, resolved_to[parsed_resolved_to.root.len..], "/\\"); |
| 1571 | 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 | 1605 | const to_rest = to_it.rest(); |
| 1574 | 1606 | if (to_it.next()) |to_component| { |
| 1575 | if (windows.eqlIgnoreCaseWtf8(from_component, to_component)) | |
| 1607 | if (eqlIgnoreCaseWtf8(from_component, to_component)) | |
| 1576 | 1608 | continue; |
| 1577 | 1609 | } |
| 1578 | 1610 | var up_index_end = "..".len; |
| 1579 | 1611 | while (from_it.next()) |_| { |
| 1580 | 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); | |
| 1583 | errdefer allocator.free(result); | |
| 1614 | const result = try gpa.alloc(u8, up_index_end + @intFromBool(to_rest.len > 0) + to_rest.len); | |
| 1615 | errdefer gpa.free(result); | |
| 1584 | 1616 | |
| 1585 | 1617 | result[0..2].* = "..".*; |
| 1586 | 1618 | var result_index: usize = 2; |
| ... | ... | @@ -1597,85 +1629,60 @@ pub fn relativeWindows(allocator: Allocator, from: []const u8, to: []const u8) ! |
| 1597 | 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 | 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 | 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 | 1646 | return switch (parsed.kind) { |
| 1609 | 1647 | .drive_absolute, |
| 1610 | 1648 | .unc_absolute, |
| 1611 | 1649 | .root_local_device, |
| 1612 | 1650 | .local_device, |
| 1613 | => try resolveWindows(allocator, &.{path}), | |
| 1614 | .relative => blk: { | |
| 1615 | const temp_allocator = temp_allocator_state.get(); | |
| 1651 | => try resolveWindows(gpa, &.{path}), | |
| 1616 | 1652 | |
| 1617 | const peb_cwd = windows.peb().ProcessParameters.CurrentDirectory.DosPath; | |
| 1618 | const cwd_w = (peb_cwd.Buffer.?)[0 .. peb_cwd.Length / 2]; | |
| 1653 | .relative => try resolveWindows(gpa, &.{ cwd, path }), | |
| 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 | 1655 | .rooted => blk: { |
| 1628 | const peb_cwd = windows.peb().ProcessParameters.CurrentDirectory.DosPath; | |
| 1629 | const cwd_w = (peb_cwd.Buffer.?)[0 .. peb_cwd.Length / 2]; | |
| 1630 | const parsed_cwd = parsePathWindows(u16, cwd_w); | |
| 1656 | const parsed_cwd = parsePathWindows(u8, cwd); | |
| 1631 | 1657 | switch (parsed_cwd.kind) { |
| 1632 | 1658 | .drive_absolute => { |
| 1633 | 1659 | var drive_buf = "_:\\".*; |
| 1634 | drive_buf[0] = @truncate(cwd_w[0]); | |
| 1635 | break :blk try resolveWindows(allocator, &.{ &drive_buf, path }); | |
| 1660 | drive_buf[0] = cwd[0]; | |
| 1661 | break :blk try resolveWindows(gpa, &.{ &drive_buf, path }); | |
| 1636 | 1662 | }, |
| 1637 | 1663 | .unc_absolute => { |
| 1638 | const temp_allocator = temp_allocator_state.get(); | |
| 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 }); | |
| 1664 | break :blk try resolveWindows(gpa, &.{ parsed_cwd.root, path }); | |
| 1645 | 1665 | }, |
| 1646 | 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 | 1670 | .drive_relative => blk: { |
| 1651 | 1671 | const temp_allocator = temp_allocator_state.get(); |
| 1652 | 1672 | const drive_cwd = drive_cwd: { |
| 1653 | const peb_cwd = windows.peb().ProcessParameters.CurrentDirectory.DosPath; | |
| 1654 | const cwd_w = (peb_cwd.Buffer.?)[0 .. peb_cwd.Length / 2]; | |
| 1655 | const parsed_cwd = parsePathWindows(u16, cwd_w); | |
| 1673 | const parsed_cwd = parsePathWindows(u8, cwd); | |
| 1656 | 1674 | |
| 1657 | 1675 | if (parsed_cwd.kind == .drive_absolute) { |
| 1658 | 1676 | const drive_letter_w = parsed_cwd.root[0]; |
| 1659 | 1677 | const drive_letters_match = drive_letter_w <= 0x7F and |
| 1660 | ascii.toUpper(@intCast(drive_letter_w)) == ascii.toUpper(parsed.root[0]); | |
| 1661 | if (drive_letters_match) { | |
| 1662 | const wtf8_len = std.unicode.calcWtf8Len(cwd_w); | |
| 1663 | const wtf8_buf = try temp_allocator.alloc(u8, wtf8_len); | |
| 1664 | assert(std.unicode.wtf16LeToWtf8(wtf8_buf, cwd_w) == wtf8_len); | |
| 1665 | break :drive_cwd wtf8_buf[0..]; | |
| 1666 | } | |
| 1667 | ||
| 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..]; | |
| 1678 | std.ascii.toUpper(@intCast(drive_letter_w)) == std.ascii.toUpper(parsed.root[0]); | |
| 1679 | if (drive_letters_match) | |
| 1680 | break :drive_cwd cwd; | |
| 1681 | ||
| 1682 | if (env_map) |m| { | |
| 1683 | if (m.get(&.{ '=', parsed.root[0], ':' })) |v| { | |
| 1684 | break :drive_cwd try temp_allocator.dupe(u8, v); | |
| 1685 | } | |
| 1679 | 1686 | } |
| 1680 | 1687 | } |
| 1681 | 1688 | |
| ... | ... | @@ -1686,16 +1693,20 @@ fn windowsResolveAgainstCwd(allocator: Allocator, path: []const u8, parsed: Wind |
| 1686 | 1693 | break :drive_cwd drive_buf; |
| 1687 | 1694 | }; |
| 1688 | 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 { | |
| 1695 | if (native_os == .windows) @compileError("this function relies on semantics that do not apply to Windows"); | |
| 1696 | ||
| 1697 | const cwd = try process.getCwdAlloc(allocator); | |
| 1698 | defer allocator.free(cwd); | |
| 1701 | /// Returns the non-absolute path from `from` to `to` according to Windows rules. | |
| 1702 | /// | |
| 1703 | /// Other than memory allocation, this is a pure function; the result solely | |
| 1704 | /// depends on the input parameters. | |
| 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 | 1710 | const resolved_from = try resolvePosix(allocator, &[_][]const u8{ cwd, from }); |
| 1700 | 1711 | defer allocator.free(resolved_from); |
| 1701 | 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 | 1747 | } |
| 1737 | 1748 | |
| 1738 | 1749 | test relative { |
| 1739 | if (native_os == .windows) { | |
| 1740 | try testRelativeWindows("c:/blah\\blah", "d:/games", "D:\\games"); | |
| 1741 | try testRelativeWindows("c:/aaaa/bbbb", "c:/aaaa", ".."); | |
| 1742 | try testRelativeWindows("c:/aaaa/bbbb", "c:/cccc", "..\\..\\cccc"); | |
| 1743 | try testRelativeWindows("c:/aaaa/bbbb", "C:/aaaa/bbbb", ""); | |
| 1744 | try testRelativeWindows("c:/aaaa/bbbb", "c:/aaaa/cccc", "..\\cccc"); | |
| 1745 | try testRelativeWindows("c:/aaaa/", "c:/aaaa/cccc", "cccc"); | |
| 1746 | try testRelativeWindows("c:/", "c:\\aaaa\\bbbb", "aaaa\\bbbb"); | |
| 1747 | try testRelativeWindows("c:/aaaa/bbbb", "d:\\", "D:\\"); | |
| 1748 | try testRelativeWindows("c:/AaAa/bbbb", "c:/aaaa/bbbb", ""); | |
| 1749 | try testRelativeWindows("c:/aaaaa/", "c:/aaaa/cccc", "..\\aaaa\\cccc"); | |
| 1750 | try testRelativeWindows("C:\\foo\\bar\\baz\\quux", "C:\\", "..\\..\\..\\.."); | |
| 1751 | try testRelativeWindows("C:\\foo\\test", "C:\\foo\\test\\bar\\package.json", "bar\\package.json"); | |
| 1752 | try testRelativeWindows("C:\\foo\\bar\\baz-quux", "C:\\foo\\bar\\baz", "..\\baz"); | |
| 1753 | try testRelativeWindows("C:\\foo\\bar\\baz", "C:\\foo\\bar\\baz-quux", "..\\baz-quux"); | |
| 1754 | try testRelativeWindows("\\\\foo\\bar", "\\\\foo\\bar\\baz", "baz"); | |
| 1755 | try testRelativeWindows("\\\\foo\\bar\\baz", "\\\\foo\\bar", ".."); | |
| 1756 | try testRelativeWindows("\\\\foo\\bar\\baz-quux", "\\\\foo\\bar\\baz", "..\\baz"); | |
| 1757 | try testRelativeWindows("\\\\foo/bar\\baz-quux", "//foo\\bar/baz", "..\\baz"); | |
| 1758 | try testRelativeWindows("\\\\foo\\bar\\baz", "\\\\foo\\bar\\baz-quux", "..\\baz-quux"); | |
| 1759 | try testRelativeWindows("C:\\baz-quux", "C:\\baz", "..\\baz"); | |
| 1760 | try testRelativeWindows("C:\\baz", "C:\\baz-quux", "..\\baz-quux"); | |
| 1761 | try testRelativeWindows("\\\\foo\\baz-quux", "\\\\foo\\baz", "\\\\foo\\baz"); | |
| 1762 | try testRelativeWindows("\\\\foo\\baz", "\\\\foo\\baz-quux", "\\\\foo\\baz-quux"); | |
| 1763 | try testRelativeWindows("C:\\baz", "\\\\foo\\bar\\baz", "\\\\foo\\bar\\baz"); | |
| 1764 | try testRelativeWindows("\\\\foo\\bar\\baz", "C:\\baz", "C:\\baz"); | |
| 1765 | ||
| 1766 | try testRelativeWindows("c:blah\\blah", "c:foo", "..\\..\\foo"); | |
| 1767 | try testRelativeWindows("c:foo", "c:foo\\bar", "bar"); | |
| 1768 | try testRelativeWindows("\\blah\\blah", "\\foo", "..\\..\\foo"); | |
| 1769 | try testRelativeWindows("\\foo", "\\foo\\bar", "bar"); | |
| 1770 | ||
| 1771 | try testRelativeWindows("a/b/c", "a\\b", ".."); | |
| 1772 | try testRelativeWindows("a/b/c", "a", "..\\.."); | |
| 1773 | try testRelativeWindows("a/b/c", "a\\b\\c\\d", "d"); | |
| 1774 | ||
| 1775 | try testRelativeWindows("\\\\FOO\\bar\\baz", "\\\\foo\\BAR\\BAZ", ""); | |
| 1776 | // Unicode-aware case-insensitive path comparison | |
| 1777 | try testRelativeWindows("\\\\кириллица\\ελληνικά\\português", "\\\\КИРИЛЛИЦА\\ΕΛΛΗΝΙΚΆ\\PORTUGUÊS", ""); | |
| 1778 | } else { | |
| 1779 | try testRelativePosix("/var/lib", "/var", ".."); | |
| 1780 | try testRelativePosix("/var/lib", "/bin", "../../bin"); | |
| 1781 | try testRelativePosix("/var/lib", "/var/lib", ""); | |
| 1782 | try testRelativePosix("/var/lib", "/var/apache", "../apache"); | |
| 1783 | try testRelativePosix("/var/", "/var/lib", "lib"); | |
| 1784 | try testRelativePosix("/", "/var/lib", "var/lib"); | |
| 1785 | try testRelativePosix("/foo/test", "/foo/test/bar/package.json", "bar/package.json"); | |
| 1786 | try testRelativePosix("/Users/a/web/b/test/mails", "/Users/a/web/b", "../.."); | |
| 1787 | try testRelativePosix("/foo/bar/baz-quux", "/foo/bar/baz", "../baz"); | |
| 1788 | try testRelativePosix("/foo/bar/baz", "/foo/bar/baz-quux", "../baz-quux"); | |
| 1789 | try testRelativePosix("/baz-quux", "/baz", "../baz"); | |
| 1790 | try testRelativePosix("/baz", "/baz-quux", "../baz-quux"); | |
| 1791 | } | |
| 1750 | try testRelativeWindows("c:/blah\\blah", "d:/games", "D:\\games"); | |
| 1751 | try testRelativeWindows("c:/aaaa/bbbb", "c:/aaaa", ".."); | |
| 1752 | try testRelativeWindows("c:/aaaa/bbbb", "c:/cccc", "..\\..\\cccc"); | |
| 1753 | try testRelativeWindows("c:/aaaa/bbbb", "C:/aaaa/bbbb", ""); | |
| 1754 | try testRelativeWindows("c:/aaaa/bbbb", "c:/aaaa/cccc", "..\\cccc"); | |
| 1755 | try testRelativeWindows("c:/aaaa/", "c:/aaaa/cccc", "cccc"); | |
| 1756 | try testRelativeWindows("c:/", "c:\\aaaa\\bbbb", "aaaa\\bbbb"); | |
| 1757 | try testRelativeWindows("c:/aaaa/bbbb", "d:\\", "D:\\"); | |
| 1758 | try testRelativeWindows("c:/AaAa/bbbb", "c:/aaaa/bbbb", ""); | |
| 1759 | try testRelativeWindows("c:/aaaaa/", "c:/aaaa/cccc", "..\\aaaa\\cccc"); | |
| 1760 | try testRelativeWindows("C:\\foo\\bar\\baz\\quux", "C:\\", "..\\..\\..\\.."); | |
| 1761 | 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"); | |
| 1763 | try testRelativeWindows("C:\\foo\\bar\\baz", "C:\\foo\\bar\\baz-quux", "..\\baz-quux"); | |
| 1764 | try testRelativeWindows("\\\\foo\\bar", "\\\\foo\\bar\\baz", "baz"); | |
| 1765 | try testRelativeWindows("\\\\foo\\bar\\baz", "\\\\foo\\bar", ".."); | |
| 1766 | try testRelativeWindows("\\\\foo\\bar\\baz-quux", "\\\\foo\\bar\\baz", "..\\baz"); | |
| 1767 | try testRelativeWindows("\\\\foo/bar\\baz-quux", "//foo\\bar/baz", "..\\baz"); | |
| 1768 | try testRelativeWindows("\\\\foo\\bar\\baz", "\\\\foo\\bar\\baz-quux", "..\\baz-quux"); | |
| 1769 | try testRelativeWindows("C:\\baz-quux", "C:\\baz", "..\\baz"); | |
| 1770 | try testRelativeWindows("C:\\baz", "C:\\baz-quux", "..\\baz-quux"); | |
| 1771 | try testRelativeWindows("\\\\foo\\baz-quux", "\\\\foo\\baz", "\\\\foo\\baz"); | |
| 1772 | try testRelativeWindows("\\\\foo\\baz", "\\\\foo\\baz-quux", "\\\\foo\\baz-quux"); | |
| 1773 | try testRelativeWindows("C:\\baz", "\\\\foo\\bar\\baz", "\\\\foo\\bar\\baz"); | |
| 1774 | try testRelativeWindows("\\\\foo\\bar\\baz", "C:\\baz", "C:\\baz"); | |
| 1775 | ||
| 1776 | try testRelativeWindows("c:blah\\blah", "c:foo", "..\\..\\foo"); | |
| 1777 | try testRelativeWindows("c:foo", "c:foo\\bar", "bar"); | |
| 1778 | try testRelativeWindows("\\blah\\blah", "\\foo", "..\\..\\foo"); | |
| 1779 | try testRelativeWindows("\\foo", "\\foo\\bar", "bar"); | |
| 1780 | ||
| 1781 | try testRelativeWindows("a/b/c", "a\\b", ".."); | |
| 1782 | try testRelativeWindows("a/b/c", "a", "..\\.."); | |
| 1783 | try testRelativeWindows("a/b/c", "a\\b\\c\\d", "d"); | |
| 1784 | ||
| 1785 | try testRelativeWindows("\\\\FOO\\bar\\baz", "\\\\foo\\BAR\\BAZ", ""); | |
| 1786 | // Unicode-aware case-insensitive path comparison | |
| 1787 | try testRelativeWindows("\\\\кириллица\\ελληνικά\\português", "\\\\КИРИЛЛИЦА\\ΕΛΛΗΝΙΚΆ\\PORTUGUÊS", ""); | |
| 1788 | ||
| 1789 | try testRelativePosix("/var/lib", "/var", ".."); | |
| 1790 | try testRelativePosix("/var/lib", "/bin", "../../bin"); | |
| 1791 | try testRelativePosix("/var/lib", "/var/lib", ""); | |
| 1792 | try testRelativePosix("/var/lib", "/var/apache", "../apache"); | |
| 1793 | try testRelativePosix("/var/", "/var/lib", "lib"); | |
| 1794 | try testRelativePosix("/", "/var/lib", "var/lib"); | |
| 1795 | 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", "../.."); | |
| 1797 | try testRelativePosix("/foo/bar/baz-quux", "/foo/bar/baz", "../baz"); | |
| 1798 | try testRelativePosix("/foo/bar/baz", "/foo/bar/baz-quux", "../baz-quux"); | |
| 1799 | try testRelativePosix("/baz-quux", "/baz", "../baz"); | |
| 1800 | try testRelativePosix("/baz", "/baz-quux", "../baz-quux"); | |
| 1792 | 1801 | } |
| 1793 | 1802 | |
| 1794 | 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 | 1805 | defer testing.allocator.free(result); |
| 1797 | 1806 | try testing.expectEqualStrings(expected_output, result); |
| 1798 | 1807 | } |
| 1799 | 1808 | |
| 1800 | 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 | 1811 | defer testing.allocator.free(result); |
| 1803 | 1812 | try testing.expectEqualStrings(expected_output, result); |
| 1804 | 1813 | } |
| ... | ... | @@ -2554,3 +2563,124 @@ pub const fmtAsUtf8Lossy = std.unicode.fmtUtf8; |
| 2554 | 2563 | /// a lossy conversion if the path contains any unpaired surrogates. |
| 2555 | 2564 | /// Unpaired surrogates are replaced by the replacement character (U+FFFD). |
| 2556 | 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 | 2927 | // Already an NT path, no need to do anything to it |
| 2928 | 2928 | break :target_path target_path; |
| 2929 | 2929 | } else { |
| 2930 | switch (getWin32PathType(u16, target_path)) { | |
| 2930 | switch (std.fs.path.getWin32PathType(u16, target_path)) { | |
| 2931 | 2931 | // Rooted paths need to avoid getting put through wToPrefixedFileW |
| 2932 | 2932 | // (and they are treated as relative in this context) |
| 2933 | 2933 | // Note: It seems that rooted paths in symbolic links are relative to |
| ... | ... | @@ -4235,7 +4235,7 @@ pub const RemoveDotDirsError = error{TooManyParentDirs}; |
| 4235 | 4235 | /// 2) all repeating back slashes have been collapsed |
| 4236 | 4236 | /// 3) the path is a relative one (does not start with a back slash) |
| 4237 | 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 | 4240 | var write_idx: usize = 0; |
| 4241 | 4241 | var read_idx: usize = 0; |
| ... | ... | @@ -4251,7 +4251,7 @@ pub fn removeDotDirsSanitized(comptime T: type, path: []T) RemoveDotDirsError!us |
| 4251 | 4251 | } |
| 4252 | 4252 | if (after_dot == '.' and (read_idx + 2 == path.len or path[read_idx + 2] == '\\')) { |
| 4253 | 4253 | if (write_idx == 0) return error.TooManyParentDirs; |
| 4254 | std.debug.assert(write_idx >= 2); | |
| 4254 | assert(write_idx >= 2); | |
| 4255 | 4255 | write_idx -= 1; |
| 4256 | 4256 | while (true) { |
| 4257 | 4257 | write_idx -= 1; |
| ... | ... | @@ -4353,7 +4353,7 @@ pub fn wToPrefixedFileW(dir: ?HANDLE, path: [:0]const u16) Wtf16ToPrefixedFileWE |
| 4353 | 4353 | path_space.data[path_space.len] = 0; |
| 4354 | 4354 | return path_space; |
| 4355 | 4355 | } else { |
| 4356 | const path_type = getWin32PathType(u16, path); | |
| 4356 | const path_type = std.fs.path.getWin32PathType(u16, path); | |
| 4357 | 4357 | var path_space: PathSpace = undefined; |
| 4358 | 4358 | if (path_type == .local_device) { |
| 4359 | 4359 | switch (getLocalDevicePathType(u16, path)) { |
| ... | ... | @@ -4491,8 +4491,8 @@ pub fn wToPrefixedFileW(dir: ?HANDLE, path: [:0]const u16) Wtf16ToPrefixedFileWE |
| 4491 | 4491 | if (path_type == .unc_absolute) { |
| 4492 | 4492 | // Now add in the UNC, the `C` should overwrite the first `\` of the |
| 4493 | 4493 | // FullPathName, ultimately resulting in `\??\UNC\<the rest of the path>` |
| 4494 | std.debug.assert(path_space.data[path_buf_offset] == '\\'); | |
| 4495 | std.debug.assert(path_space.data[path_buf_offset + 1] == '\\'); | |
| 4494 | assert(path_space.data[path_buf_offset] == '\\'); | |
| 4495 | assert(path_space.data[path_buf_offset + 1] == '\\'); | |
| 4496 | 4496 | const unc = [_]u16{ 'U', 'N', 'C' }; |
| 4497 | 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 | 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 | 4503 | /// Returns true if the path starts with `\??\`, which is indicative of an NT path |
| 4625 | 4504 | /// but is not enough to fully distinguish between NT paths and Win32 paths, as |
| 4626 | 4505 | /// `\??\` is not actually a distinct prefix but rather the path to a special virtual |
| ... | ... | @@ -4663,7 +4542,7 @@ const LocalDevicePathType = enum { |
| 4663 | 4542 | /// Asserts `path` is of type `Win32PathType.local_device`. |
| 4664 | 4543 | fn getLocalDevicePathType(comptime T: type, path: []const T) LocalDevicePathType { |
| 4665 | 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 | 4548 | const backslash = mem.nativeToLittle(T, '\\'); |
lib/std/process/Args.zig+2-1| ... | ... | @@ -12,6 +12,7 @@ vector: Vector, |
| 12 | 12 | |
| 13 | 13 | pub const Vector = switch (native_os) { |
| 14 | 14 | .windows => []const u16, // WTF-16 encoded |
| 15 | .freestanding, .other => void, | |
| 15 | 16 | else => []const [*:0]const u8, |
| 16 | 17 | }; |
| 17 | 18 | |
| ... | ... | @@ -57,7 +58,7 @@ pub const Iterator = struct { |
| 57 | 58 | /// Returned slice is pointing to the iterator's internal buffer. |
| 58 | 59 | /// On Windows, the result is encoded as [WTF-8](https://wtf-8.codeberg.page/). |
| 59 | 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 | 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 | 21 | /// On Windows this is the hProcess. |
| 22 | 22 | /// On POSIX this is the pid. |
| 23 | 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 | 25 | /// The writing end of the child process's standard input pipe. |
| 26 | 26 | /// Usage requires `process.SpawnOptions.StdIo.pipe`. |
| 27 | 27 | stdin: ?File, |
lib/std/process/Environ.zig+9-7| ... | ... | @@ -12,11 +12,6 @@ const posix = std.posix; |
| 12 | 12 | const mem = std.mem; |
| 13 | 13 | |
| 14 | 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 | 15 | block: Block, |
| 21 | 16 | |
| 22 | 17 | 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 | 30 | pub const Block = switch (native_os) { |
| 30 | .windows => [*:0]const u16, | |
| 31 | .windows => void, | |
| 31 | 32 | .wasi => switch (builtin.link_libc) { |
| 32 | 33 | false => void, |
| 33 | 34 | true => [:null]const ?[*:0]const u8, |
| 34 | 35 | }, |
| 36 | .freestanding, .other => void, | |
| 35 | 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 | 347 | errdefer result.deinit(); |
| 346 | 348 | |
| 347 | 349 | if (native_os == .windows) { |
| 348 | const ptr = env.block; | |
| 350 | const ptr = std.os.windows.peb().ProcessParameters.Environment; | |
| 349 | 351 | |
| 350 | 352 | var i: usize = 0; |
| 351 | 353 | while (ptr[i] != 0) { |
lib/std/start.zig+52-109| ... | ... | @@ -11,118 +11,63 @@ const native_os = builtin.os.tag; |
| 11 | 11 | |
| 12 | 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 | 14 | comptime { |
| 29 | 15 | // No matter what, we import the root file, so that any export, test, comptime |
| 30 | 16 | // decls there get run. |
| 31 | 17 | _ = root; |
| 32 | 18 | |
| 33 | if (simplified_logic) { | |
| 34 | if (builtin.output_mode == .Exe) { | |
| 35 | if ((builtin.link_libc or builtin.object_format == .c) and @hasDecl(root, "main")) { | |
| 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 | } | |
| 19 | if (builtin.output_mode == .Lib and builtin.link_mode == .dynamic) { | |
| 20 | if (native_os == .windows and !@hasDecl(root, "_DllMainCRTStartup")) { | |
| 21 | @export(&_DllMainCRTStartup, .{ .name = "_DllMainCRTStartup" }); | |
| 51 | 22 | } |
| 52 | } else { | |
| 53 | if (builtin.output_mode == .Lib and builtin.link_mode == .dynamic) { | |
| 54 | if (native_os == .windows and !@hasDecl(root, "_DllMainCRTStartup")) { | |
| 55 | @export(&_DllMainCRTStartup, .{ .name = "_DllMainCRTStartup" }); | |
| 23 | } else if (builtin.output_mode == .Exe or @hasDecl(root, "main")) { | |
| 24 | if (builtin.link_libc and @hasDecl(root, "main")) { | |
| 25 | if (native_arch.isWasm()) { | |
| 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")) { | |
| 58 | if (builtin.link_libc and @hasDecl(root, "main")) { | |
| 59 | if (native_arch.isWasm()) { | |
| 60 | @export(&mainWithoutEnv, .{ .name = "__main_argc_argv" }); | |
| 61 | } else if (!@typeInfo(@TypeOf(root.main)).@"fn".calling_convention.eql(.c)) { | |
| 62 | @export(&main, .{ .name = "main" }); | |
| 63 | } | |
| 64 | } else if (native_os == .windows and builtin.link_libc and @hasDecl(root, "wWinMain")) { | |
| 65 | if (!@typeInfo(@TypeOf(root.wWinMain)).@"fn".calling_convention.eql(.c)) { | |
| 66 | @export(&wWinMain, .{ .name = "wWinMain" }); | |
| 67 | } | |
| 68 | } else if (native_os == .windows) { | |
| 69 | if (!@hasDecl(root, "WinMain") and !@hasDecl(root, "WinMainCRTStartup") and | |
| 70 | !@hasDecl(root, "wWinMain") and !@hasDecl(root, "wWinMainCRTStartup")) | |
| 71 | { | |
| 72 | @export(&WinStartup, .{ .name = "wWinMainCRTStartup" }); | |
| 73 | } else if (@hasDecl(root, "WinMain") and !@hasDecl(root, "WinMainCRTStartup") and | |
| 74 | !@hasDecl(root, "wWinMain") and !@hasDecl(root, "wWinMainCRTStartup")) | |
| 75 | { | |
| 76 | @compileError("WinMain not supported; declare wWinMain or main instead"); | |
| 77 | } else if (@hasDecl(root, "wWinMain") and !@hasDecl(root, "wWinMainCRTStartup") and | |
| 78 | !@hasDecl(root, "WinMain") and !@hasDecl(root, "WinMainCRTStartup")) | |
| 79 | { | |
| 80 | @export(&wWinMainCRTStartup, .{ .name = "wWinMainCRTStartup" }); | |
| 81 | } | |
| 82 | } else if (native_os == .uefi) { | |
| 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) { | |
| 30 | } else if (native_os == .windows and builtin.link_libc and @hasDecl(root, "wWinMain")) { | |
| 31 | if (!@typeInfo(@TypeOf(root.wWinMain)).@"fn".calling_convention.eql(.c)) { | |
| 32 | @export(&wWinMain, .{ .name = "wWinMain" }); | |
| 33 | } | |
| 34 | } else if (native_os == .windows) { | |
| 35 | if (!@hasDecl(root, "WinMain") and !@hasDecl(root, "WinMainCRTStartup") and | |
| 36 | !@hasDecl(root, "wWinMain") and !@hasDecl(root, "wWinMainCRTStartup")) | |
| 37 | { | |
| 38 | @export(&WinStartup, .{ .name = "wWinMainCRTStartup" }); | |
| 39 | } else if (@hasDecl(root, "WinMain") and !@hasDecl(root, "WinMainCRTStartup") and | |
| 40 | !@hasDecl(root, "wWinMain") and !@hasDecl(root, "wWinMainCRTStartup")) | |
| 41 | { | |
| 42 | @compileError("WinMain not supported; declare wWinMain or main instead"); | |
| 43 | } else if (@hasDecl(root, "wWinMain") and !@hasDecl(root, "wWinMainCRTStartup") and | |
| 44 | !@hasDecl(root, "WinMain") and !@hasDecl(root, "WinMainCRTStartup")) | |
| 45 | { | |
| 46 | @export(&wWinMainCRTStartup, .{ .name = "wWinMainCRTStartup" }); | |
| 47 | } | |
| 48 | } else if (native_os == .uefi) { | |
| 49 | if (!@hasDecl(root, "EfiMain")) @export(&EfiMain, .{ .name = "EfiMain" }); | |
| 50 | } else if (native_os == .wasi) { | |
| 51 | const wasm_start_sym = switch (builtin.wasi_exec_model) { | |
| 52 | .reactor => "_initialize", | |
| 53 | .command => "_start", | |
| 54 | }; | |
| 55 | if (!@hasDecl(root, wasm_start_sym) and @hasDecl(root, "main")) { | |
| 95 | 56 | // Only call main when defined. For WebAssembly it's allowed to pass `-fno-entry` in which |
| 96 | 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 }); | |
| 98 | } else switch (native_os) { | |
| 99 | .other, .freestanding, .@"3ds", .vita => {}, | |
| 100 | else => if (!@hasDecl(root, start_sym_name)) @export(&_start, .{ .name = start_sym_name }), | |
| 58 | @export(&wasi_start, .{ .name = wasm_start_sym }); | |
| 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 | 71 | fn _DllMainCRTStartup( |
| 127 | 72 | hinstDLL: std.os.windows.HINSTANCE, |
| 128 | 73 | fdwReason: std.os.windows.DWORD, |
| ... | ... | @@ -142,15 +87,15 @@ fn _DllMainCRTStartup( |
| 142 | 87 | fn wasm_freestanding_start() callconv(.c) void { |
| 143 | 88 | // This is marked inline because for some reason LLVM in |
| 144 | 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 | 93 | fn wasi_start() callconv(.c) void { |
| 149 | 94 | // The function call is marked inline because for some reason LLVM in |
| 150 | 95 | // release mode fails to inline it, and we want fewer call frames in stack traces. |
| 151 | 96 | switch (builtin.wasi_exec_model) { |
| 152 | .reactor => _ = @call(.always_inline, callMain, .{}), | |
| 153 | .command => std.os.wasi.proc_exit(@call(.always_inline, callMain, .{})), | |
| 97 | .reactor => _ = @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 | 469 | |
| 525 | 470 | std.debug.maybeEnableSegfaultHandler(); |
| 526 | 471 | |
| 527 | const peb = std.os.windows.peb(); | |
| 528 | 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( | |
| 531 | cmd_line.Buffer.?[0..@divExact(cmd_line.Length, 2)], | |
| 532 | peb.ProcessParameters.Environment, | |
| 533 | )); | |
| 475 | std.os.windows.ntdll.RtlExitUserProcess(callMain(cmd_line_w, {})); | |
| 534 | 476 | } |
| 535 | 477 | |
| 536 | 478 | fn wWinMainCRTStartup() callconv(.withStackAlign(.c, 1)) noreturn { |
| ... | ... | @@ -637,6 +579,7 @@ fn posixCallMainAndExit(argc_argv_ptr: [*]usize) callconv(.c) noreturn { |
| 637 | 579 | } |
| 638 | 580 | |
| 639 | 581 | fn expandStackSize(phdrs: []elf.Phdr) void { |
| 582 | @disableInstrumentation(); | |
| 640 | 583 | for (phdrs) |*phdr| { |
| 641 | 584 | switch (phdr.p_type) { |
| 642 | 585 | elf.PT_GNU_STACK => { |
| ... | ... | @@ -674,7 +617,7 @@ fn expandStackSize(phdrs: []elf.Phdr) void { |
| 674 | 617 | inline fn callMainWithArgs(argc: usize, argv: [*][*:0]u8, envp: [:null]?[*:0]u8) u8 { |
| 675 | 618 | if (std.Options.debug_threaded_io) |t| { |
| 676 | 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 | 622 | std.debug.maybeEnableSegfaultHandler(); |
| 680 | 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 | 678 | defer arena_allocator.deinit(); |
| 736 | 679 | |
| 737 | 680 | var threaded: std.Io.Threaded = .init(gpa, .{ |
| 738 | .argv0 = if (@sizeOf(std.Io.Threaded.Argv0) != 0) .{ .value = args[0] } else .{}, | |
| 739 | .environ = .{ .block = environ }, | |
| 681 | .argv0 = .init(.{ .value = args }), | |
| 682 | .environ = .{ .process_environ = .{ .block = environ } }, | |
| 740 | 683 | }); |
| 741 | 684 | defer threaded.deinit(); |
| 742 | 685 |