| ... | @@ -702,8 +702,8 @@ pub fn getEnvMap(allocator: *Allocator) !BufMap { | ... | @@ -702,8 +702,8 @@ pub fn getEnvMap(allocator: *Allocator) !BufMap { |
| 702 | errdefer result.deinit(); | 702 | errdefer result.deinit(); |
| 703 | | 703 | |
| 704 | if (is_windows) { | 704 | if (is_windows) { |
| 705 | const ptr = windows.GetEnvironmentStringsA() orelse return error.OutOfMemory; | 705 | const ptr = windows.GetEnvironmentStringsW() orelse return error.OutOfMemory; |
| 706 | defer assert(windows.FreeEnvironmentStringsA(ptr) != 0); | 706 | defer assert(windows.FreeEnvironmentStringsW(ptr) != 0); |
| 707 | | 707 | |
| 708 | var i: usize = 0; | 708 | var i: usize = 0; |
| 709 | while (true) { | 709 | while (true) { |
| ... | @@ -712,17 +712,50 @@ pub fn getEnvMap(allocator: *Allocator) !BufMap { | ... | @@ -712,17 +712,50 @@ pub fn getEnvMap(allocator: *Allocator) !BufMap { |
| 712 | const key_start = i; | 712 | const key_start = i; |
| 713 | | 713 | |
| 714 | while (ptr[i] != 0 and ptr[i] != '=') : (i += 1) {} | 714 | while (ptr[i] != 0 and ptr[i] != '=') : (i += 1) {} |
| 715 | const key = ptr[key_start..i]; | 715 | |
| | 716 | const stack_var_len = 50; |
| | 717 | const key_slice = ptr[key_start..i]; |
| | 718 | var key: []u8 = undefined; |
| | 719 | var heap_key = false; |
| | 720 | |
| | 721 | // parse the key on the stack if smaller than 'stack_var_len' |
| | 722 | if (key_slice.len < stack_var_len-@sizeOf(usize)) { |
| | 723 | var buf = []u8{0} ** stack_var_len; |
| | 724 | var fallocator = &std.heap.FixedBufferAllocator.init(buf[0..]).allocator; |
| | 725 | key = try std.unicode.utf16leToUtf8Alloc(fallocator, key_slice); |
| | 726 | } else { |
| | 727 | key = try std.unicode.utf16leToUtf8Alloc(allocator, key_slice); |
| | 728 | heap_key = true; // key needs to outlive this scope, so we cannot defer |
| | 729 | } |
| 716 | | 730 | |
| 717 | if (ptr[i] == '=') i += 1; | 731 | if (ptr[i] == '=') i += 1; |
| 718 | | 732 | |
| 719 | const value_start = i; | 733 | const value_start = i; |
| 720 | while (ptr[i] != 0) : (i += 1) {} | 734 | while (ptr[i] != 0) : (i += 1) {} |
| 721 | const value = ptr[value_start..i]; | 735 | |
| | 736 | const value_slice = ptr[value_start..i]; |
| | 737 | var value: []u8 = undefined; |
| | 738 | var heap_value = false; |
| | 739 | |
| | 740 | if (value_slice.len < stack_var_len-@sizeOf(usize)) { |
| | 741 | var buf = []u8{0} ** stack_var_len; |
| | 742 | var fallocator = &std.heap.FixedBufferAllocator.init(buf[0..]).allocator; |
| | 743 | value = try std.unicode.utf16leToUtf8Alloc(fallocator, value_slice); |
| | 744 | } else { |
| | 745 | value = try std.unicode.utf16leToUtf8Alloc(allocator, value_slice); |
| | 746 | heap_value = true; // value needs to outlive this scope, so we cannot defer |
| | 747 | } |
| 722 | | 748 | |
| 723 | i += 1; // skip over null byte | 749 | i += 1; // skip over null byte |
| 724 | | 750 | |
| 725 | try result.set(key, value); | 751 | try result.set(key, value); |
| | 752 | |
| | 753 | if (heap_key) { |
| | 754 | allocator.free(key); |
| | 755 | } |
| | 756 | if (heap_value) { |
| | 757 | allocator.free(value); |
| | 758 | } |
| 726 | } | 759 | } |
| 727 | } else { | 760 | } else { |
| 728 | for (posix_environ_raw) |ptr| { | 761 | for (posix_environ_raw) |ptr| { |
| ... | @@ -740,6 +773,19 @@ pub fn getEnvMap(allocator: *Allocator) !BufMap { | ... | @@ -740,6 +773,19 @@ pub fn getEnvMap(allocator: *Allocator) !BufMap { |
| 740 | } | 773 | } |
| 741 | } | 774 | } |
| 742 | | 775 | |
| | 776 | test "os.getEnvMap" { |
| | 777 | var env = try getEnvMap(std.debug.global_allocator); |
| | 778 | var seen_path = false; |
| | 779 | var it = env.iterator(); |
| | 780 | while (it.next()) |pair| { |
| | 781 | if (mem.eql(u8, pair.key, "PATH")) { |
| | 782 | seen_path = true; |
| | 783 | } |
| | 784 | } |
| | 785 | |
| | 786 | assert(seen_path == true); |
| | 787 | } |
| | 788 | |
| 743 | /// TODO make this go through libc when we have it | 789 | /// TODO make this go through libc when we have it |
| 744 | pub fn getEnvPosix(key: []const u8) ?[]const u8 { | 790 | pub fn getEnvPosix(key: []const u8) ?[]const u8 { |
| 745 | for (posix_environ_raw) |ptr| { | 791 | for (posix_environ_raw) |ptr| { |
| ... | @@ -760,21 +806,27 @@ pub fn getEnvPosix(key: []const u8) ?[]const u8 { | ... | @@ -760,21 +806,27 @@ pub fn getEnvPosix(key: []const u8) ?[]const u8 { |
| 760 | pub const GetEnvVarOwnedError = error{ | 806 | pub const GetEnvVarOwnedError = error{ |
| 761 | OutOfMemory, | 807 | OutOfMemory, |
| 762 | EnvironmentVariableNotFound, | 808 | EnvironmentVariableNotFound, |
| | 809 | DanglingSurrogateHalf, |
| | 810 | ExpectedSecondSurrogateHalf, |
| | 811 | UnexpectedSecondSurrogateHalf, |
| | 812 | |
| | 813 | /// See https://github.com/ziglang/zig/issues/1774 |
| | 814 | InvalidUtf8, |
| 763 | }; | 815 | }; |
| 764 | | 816 | |
| 765 | /// Caller must free returned memory. | 817 | /// Caller must free returned memory. |
| 766 | /// TODO make this go through libc when we have it | 818 | /// TODO make this go through libc when we have it |
| 767 | pub fn getEnvVarOwned(allocator: *mem.Allocator, key: []const u8) GetEnvVarOwnedError![]u8 { | 819 | pub fn getEnvVarOwned(allocator: *mem.Allocator, key: []const u8) GetEnvVarOwnedError![]u8 { |
| 768 | if (is_windows) { | 820 | if (is_windows) { |
| 769 | const key_with_null = try cstr.addNullByte(allocator, key); | 821 | const key_with_null = try std.unicode.utf8ToUtf16LeWithNull(allocator, key); |
| 770 | defer allocator.free(key_with_null); | 822 | defer allocator.free(key_with_null); |
| 771 | | 823 | |
| 772 | var buf = try allocator.alloc(u8, 256); | 824 | var buf = try allocator.alloc(u16, 256); |
| 773 | errdefer allocator.free(buf); | 825 | defer allocator.free(buf); |
| 774 | | 826 | |
| 775 | while (true) { | 827 | while (true) { |
| 776 | const windows_buf_len = math.cast(windows.DWORD, buf.len) catch return error.OutOfMemory; | 828 | const windows_buf_len = math.cast(windows.DWORD, buf.len) catch return error.OutOfMemory; |
| 777 | const result = windows.GetEnvironmentVariableA(key_with_null.ptr, buf.ptr, windows_buf_len); | 829 | const result = windows.GetEnvironmentVariableW(key_with_null.ptr, buf.ptr, windows_buf_len); |
| 778 | | 830 | |
| 779 | if (result == 0) { | 831 | if (result == 0) { |
| 780 | const err = windows.GetLastError(); | 832 | const err = windows.GetLastError(); |
| ... | @@ -788,11 +840,11 @@ pub fn getEnvVarOwned(allocator: *mem.Allocator, key: []const u8) GetEnvVarOwned | ... | @@ -788,11 +840,11 @@ pub fn getEnvVarOwned(allocator: *mem.Allocator, key: []const u8) GetEnvVarOwned |
| 788 | } | 840 | } |
| 789 | | 841 | |
| 790 | if (result > buf.len) { | 842 | if (result > buf.len) { |
| 791 | buf = try allocator.realloc(u8, buf, result); | 843 | buf = try allocator.realloc(u16, buf, result); |
| 792 | continue; | 844 | continue; |
| 793 | } | 845 | } |
| 794 | | 846 | |
| 795 | return allocator.shrink(u8, buf, result); | 847 | return try std.unicode.utf16leToUtf8Alloc(allocator, buf); |
| 796 | } | 848 | } |
| 797 | } else { | 849 | } else { |
| 798 | const result = getEnvPosix(key) orelse return error.EnvironmentVariableNotFound; | 850 | const result = getEnvPosix(key) orelse return error.EnvironmentVariableNotFound; |
| ... | @@ -800,6 +852,15 @@ pub fn getEnvVarOwned(allocator: *mem.Allocator, key: []const u8) GetEnvVarOwned | ... | @@ -800,6 +852,15 @@ pub fn getEnvVarOwned(allocator: *mem.Allocator, key: []const u8) GetEnvVarOwned |
| 800 | } | 852 | } |
| 801 | } | 853 | } |
| 802 | | 854 | |
| | 855 | test "os.getEnvVarOwned" { |
| | 856 | switch (builtin.os) { |
| | 857 | builtin.Os.windows, builtin.Os.linux, builtin.Os.macosx, |
| | 858 | builtin.Os.ios => _ = try getEnvVarOwned(debug.global_allocator, "PATH"), |
| | 859 | else => @compileError("unimplemented"), |
| | 860 | } |
| | 861 | } |
| | 862 | |
| | 863 | |
| 803 | /// Caller must free the returned memory. | 864 | /// Caller must free the returned memory. |
| 804 | pub fn getCwdAlloc(allocator: *Allocator) ![]u8 { | 865 | pub fn getCwdAlloc(allocator: *Allocator) ![]u8 { |
| 805 | var buf: [MAX_PATH_BYTES]u8 = undefined; | 866 | var buf: [MAX_PATH_BYTES]u8 = undefined; |