Creates a null-delimited environment variable block in the format expected by POSIX, from a different one.
pub fn createWindowsBlock(
existing: Environ,
gpa: Allocator,
options: CreateWindowsBlockOptions,
) Allocator.Error!WindowsBlock
pub fn createWindowsBlock(
existing: Environ,
gpa: Allocator,
options: CreateWindowsBlockOptions,
) Allocator.Error!WindowsBlock {
if (!existing.block.use_global) return .{
.slice = try gpa.dupeSentinel(u16, WindowsBlock.empty.slice, 0),
};
const peb = std.os.windows.peb();
assert(std.os.windows.ntdll.RtlEnterCriticalSection(peb.FastPebLock) == .SUCCESS);
defer assert(std.os.windows.ntdll.RtlLeaveCriticalSection(peb.FastPebLock) == .SUCCESS);
const existing_block = peb.ProcessParameters.Environment;
var ranges: [2]struct { start: usize, end: usize } = undefined;
var ranges_len: usize = 0;
ranges[ranges_len].start = 0;
const zig_progress_key = [_]u16{ 'Z', 'I', 'G', '_', 'P', 'R', 'O', 'G', 'R', 'E', 'S', 'S', '=' };
const needed_len = needed_len: {
var needed_len: usize = "\x00".len;
if (options.zig_progress_handle) |handle| if (handle != std.os.windows.INVALID_HANDLE_VALUE) {
needed_len += std.fmt.count("ZIG_PROGRESS={d}\x00", .{@intFromPtr(handle)});
};
var i: usize = 0;
while (existing_block[i] != 0) {
const start = i;
const entry = mem.sliceTo(existing_block[start..], 0);
i += entry.len + "\x00".len;
if (options.zig_progress_handle != null and entry.len >= zig_progress_key.len and
std.os.windows.eqlIgnoreCaseWtf16(entry[0..zig_progress_key.len], &zig_progress_key))
{
ranges[ranges_len].end = start;
ranges_len += 1;
ranges[ranges_len].start = i;
} else needed_len += entry.len + "\x00".len;
}
ranges[ranges_len].end = i;
ranges_len += 1;
break :needed_len @max("\x00\x00".len, needed_len);
};
const block = try gpa.alloc(u16, needed_len);
errdefer gpa.free(block);
var i: usize = 0;
if (options.zig_progress_handle) |handle| if (handle != std.os.windows.INVALID_HANDLE_VALUE) {
@memcpy(block[i..][0..zig_progress_key.len], &zig_progress_key);
i += zig_progress_key.len;
var value_buf: [std.fmt.count("{d}", .{std.math.maxInt(usize)})]u8 = undefined;
const value = std.fmt.bufPrint(&value_buf, "{d}", .{@intFromPtr(handle)}) catch unreachable;
for (block[i..][0..value.len], value) |*r, v| r.* = v;
i += value.len;
block[i] = 0;
i += 1;
};
for (ranges[0..ranges_len]) |range| {
const range_len = range.end - range.start;
@memcpy(block[i..][0..range_len], existing_block[range.start..range.end]);
i += range_len;
}
// An empty environment is a special case that requires a redundant
// NUL terminator. CreateProcess will read the second code unit even
// though theoretically the first should be enough to recognize that the
// environment is empty (see https://nullprogram.com/blog/2023/08/23/)
for (0..2) |_| {
block[i] = 0;
i += 1;
if (i >= 2) break;
} else unreachable;
assert(i == block.len);
return .{ .slice = block[0 .. i - 1 :0] };
}