@@ -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+
4274pub 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
201230fn 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