Skip to content

Commit 01c9a05

Browse files
fix: windows home and compspec (#95)
1 parent ccf1dbd commit 01c9a05

1 file changed

Lines changed: 75 additions & 70 deletions

File tree

src/main.zig

Lines changed: 75 additions & 70 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,38 @@ const AliasBuffers = struct {
3939
exec_arguments_count: u32 = 0,
4040
};
4141

42+
/// Helper to check if environment variable exists on Windows (Zig 0.15.1 compatible)
43+
fn hasWindowsEnvVar(var_name: []const u8) bool {
44+
if (builtin.os.tag != .windows) return false;
45+
46+
var arena = std.heap.ArenaAllocator.init(std.heap.page_allocator);
47+
defer arena.deinit();
48+
49+
const result = std.process.getEnvVarOwned(arena.allocator(), var_name) catch |err| switch (err) {
50+
error.EnvironmentVariableNotFound => return false,
51+
else => return false, // Assume not found on any error
52+
};
53+
54+
return result.len > 0;
55+
}
56+
57+
/// Helper to get environment variable on Windows (Zig 0.15.1 compatible)
58+
fn getWindowsEnvVar(allocator: std.mem.Allocator, var_name: []const u8, buffer: []u8) !?[]const u8 {
59+
if (builtin.os.tag != .windows) return null;
60+
61+
const result = std.process.getEnvVarOwned(allocator, var_name) catch |err| switch (err) {
62+
error.EnvironmentVariableNotFound => return null,
63+
else => return err,
64+
};
65+
66+
if (result.len >= buffer.len) {
67+
return error.BufferTooSmall;
68+
}
69+
70+
@memcpy(buffer[0..result.len], result);
71+
return buffer[0..result.len];
72+
}
73+
4274
pub fn main() !void {
4375
// Collect command line arguments into a fixed buffer.
4476
var arguments_buffer: [limits.limits.arguments_maximum][]const u8 = undefined;
@@ -123,10 +155,7 @@ pub fn main() !void {
123155

124156
// If ZVM_DEBUG environment variable is set, print resource usage
125157
const has_debug = if (builtin.os.tag == .windows) blk: {
126-
var temp_buffer: [16]u16 = undefined;
127-
const zvm_debug_w = std.unicode.utf8ToUtf16LeStringLiteral("ZVM_DEBUG");
128-
const len = std.os.windows.kernel32.GetEnvironmentVariableW(zvm_debug_w, &temp_buffer, temp_buffer.len);
129-
break :blk len > 0;
158+
break :blk hasWindowsEnvVar("ZVM_DEBUG");
130159
} else blk: {
131160
break :blk std.posix.getenv("ZVM_DEBUG") != null;
132161
};
@@ -200,53 +229,45 @@ fn handle_alias(program_name: []const u8, remaining_arguments: [][]const u8) !vo
200229
/// Get the HOME environment variable and copy it to the static buffer
201230
fn get_home_path(alias_buffers: *AliasBuffers) ![]const u8 {
202231
if (builtin.os.tag == .windows) {
203-
// On Windows, we need to handle UTF-16 to UTF-8 conversion
204-
// Use a temporary buffer for the conversion
205-
var temp_buffer: [limits.limits.home_dir_length_maximum * 2]u16 = undefined;
206-
const userprofile_w = std.unicode.utf8ToUtf16LeStringLiteral("USERPROFILE");
207-
208-
const len = std.os.windows.kernel32.GetEnvironmentVariableW(userprofile_w, &temp_buffer, temp_buffer.len);
209-
if (len == 0) {
210-
std.log.err("USERPROFILE environment variable not set. Please set USERPROFILE to your home directory", .{});
211-
return error.HomeNotFound;
212-
}
213-
214-
// Convert UTF-16 to UTF-8 directly into our buffer
215-
const utf16_slice = temp_buffer[0..len];
216-
// Try to convert directly to see how many bytes we need
217-
const utf8_len = std.unicode.utf16LeToUtf8(alias_buffers.home[0..], utf16_slice) catch |err| {
218-
if (err == error.BufferTooSmall) {
219-
std.log.err("Home path too long for buffer", .{});
220-
return error.HomePathTooLong;
232+
// On Windows, use std.process.getEnvVarOwned for proper environment variable access
233+
// This handles UTF-16/UTF-8 conversion internally
234+
var arena = std.heap.ArenaAllocator.init(std.heap.page_allocator);
235+
defer arena.deinit();
236+
237+
const home = std.process.getEnvVarOwned(arena.allocator(), "USERPROFILE") catch |err| {
238+
switch (err) {
239+
error.EnvironmentVariableNotFound => {
240+
std.log.err("USERPROFILE environment variable not set. Please set USERPROFILE to your home directory", .{});
241+
return error.HomeNotFound;
242+
},
243+
else => {
244+
std.log.err("Error reading USERPROFILE: {}", .{err});
245+
return error.HomeNotFound;
246+
},
221247
}
222-
return error.InvalidUtf8;
223248
};
224-
return alias_buffers.home[0..utf8_len];
249+
250+
if (home.len >= alias_buffers.home.len) {
251+
std.log.err("Home path too long for buffer", .{});
252+
return error.HomePathTooLong;
253+
}
254+
255+
@memcpy(alias_buffers.home[0..home.len], home);
256+
return alias_buffers.home[0..home.len];
225257
} else {
226-
// On POSIX, getenv returns a pointer without allocation
258+
// Unix-like systems
227259
const home = std.posix.getenv("HOME") orelse {
228260
std.log.err("HOME environment variable not set. Please set HOME to your home directory", .{});
229261
return error.HomeNotFound;
230262
};
231263

232-
// Validate home path
233-
std.debug.assert(home.len > 0);
234-
if (home.len > alias_buffers.home.len) {
235-
std.log.err("Home path too long: got {d} bytes, maximum is {d} bytes. Path: '{s}'", .{
236-
home.len,
237-
alias_buffers.home.len,
238-
home,
239-
});
264+
if (home.len >= alias_buffers.home.len) {
265+
std.log.err("Home path too long for buffer", .{});
240266
return error.HomePathTooLong;
241267
}
242268

243269
@memcpy(alias_buffers.home[0..home.len], home);
244-
const home_slice = alias_buffers.home[0..home.len];
245-
246-
std.debug.assert(home_slice.len == home.len);
247-
std.debug.assert(std.mem.eql(u8, home_slice, home));
248-
249-
return home_slice;
270+
return alias_buffers.home[0..home.len];
250271
}
251272
}
252273

@@ -257,26 +278,17 @@ fn get_zvm_home_path(alias_buffers: *AliasBuffers, home_slice: []const u8) ![]co
257278

258279
if (builtin.os.tag == .windows) {
259280
// On Windows, check for ZVM_HOME environment variable
260-
var temp_buffer: [limits.limits.home_dir_length_maximum * 2]u16 = undefined;
261-
const zvm_home_w = std.unicode.utf8ToUtf16LeStringLiteral("ZVM_HOME");
262-
263-
const len = std.os.windows.kernel32.GetEnvironmentVariableW(zvm_home_w, &temp_buffer, temp_buffer.len);
264-
if (len > 0 and len < temp_buffer.len) {
265-
// Convert UTF-16 to UTF-8
266-
const utf16_slice = temp_buffer[0..len];
267-
const utf8_len = std.unicode.utf16LeToUtf8(alias_buffers.zvm_home[0..], utf16_slice) catch {
268-
// If conversion fails, use default
269-
var fixed_buffer_stream = std.io.fixedBufferStream(&alias_buffers.zvm_home);
270-
try fixed_buffer_stream.writer().print("{s}\\.zm", .{home_slice});
271-
return fixed_buffer_stream.getWritten();
272-
};
273-
return alias_buffers.zvm_home[0..utf8_len];
274-
}
281+
var arena = std.heap.ArenaAllocator.init(std.heap.page_allocator);
282+
defer arena.deinit();
275283

276-
// No ZVM_HOME or too long, use default
277-
var fixed_buffer_stream = std.io.fixedBufferStream(&alias_buffers.zvm_home);
278-
try fixed_buffer_stream.writer().print("{s}\\.zm", .{home_slice});
279-
return fixed_buffer_stream.getWritten();
284+
if (getWindowsEnvVar(arena.allocator(), "ZVM_HOME", &alias_buffers.zvm_home) catch null) |zvm_home| {
285+
return zvm_home;
286+
} else {
287+
// No ZVM_HOME or error, use default
288+
var fixed_buffer_stream = std.io.fixedBufferStream(&alias_buffers.zvm_home);
289+
try fixed_buffer_stream.writer().print("{s}\\.zm", .{home_slice});
290+
return fixed_buffer_stream.getWritten();
291+
}
280292
} else {
281293
// On POSIX, use getenv without allocation
282294
if (std.posix.getenv("ZVM_HOME")) |zh| {
@@ -641,18 +653,11 @@ fn print_env_setup(ctx: *context.CliContext, shell: ?[]const u8) !void {
641653
const shell_type = if (shell) |s| s else blk: {
642654
if (builtin.os.tag == .windows) {
643655
// On Windows, check COMSPEC
644-
var temp_buffer: [512]u16 = undefined;
645-
const comspec_w = std.unicode.utf8ToUtf16LeStringLiteral("COMSPEC");
646-
647-
const len = std.os.windows.kernel32.GetEnvironmentVariableW(comspec_w, &temp_buffer, temp_buffer.len);
648-
if (len > 0 and len < temp_buffer.len) {
649-
// Convert UTF-16 to UTF-8 into path buffer
650-
const utf16_slice = temp_buffer[0..len];
651-
var utf8_buffer: [limits.limits.temp_buffer_size]u8 = undefined;
652-
const utf8_len = std.unicode.utf16LeToUtf8(utf8_buffer[0..], utf16_slice) catch {
653-
break :blk "cmd"; // Default for Windows
654-
};
655-
const shell_path = utf8_buffer[0..utf8_len];
656+
var arena = std.heap.ArenaAllocator.init(std.heap.page_allocator);
657+
defer arena.deinit();
658+
659+
var utf8_buffer: [limits.limits.temp_buffer_size]u8 = undefined;
660+
if (getWindowsEnvVar(arena.allocator(), "COMSPEC", &utf8_buffer) catch null) |shell_path| {
656661
const shell_name = std.fs.path.basename(shell_path);
657662
if (std.mem.indexOf(u8, shell_name, "powershell") != null) {
658663
break :blk "powershell";

0 commit comments

Comments
 (0)