aboutsummaryrefslogtreecommitdiff
path: root/src/javascript/jsc/node_env_buf_map.zig
blob: 73bc025b60fa98b7493bd51ce2dab89ed9981487 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
const std = @import("std");
usingnamespace @import("../../global.zig");

// This makes it so we get the defines already formatted from the user's environment with the "process.env." prefix set
// This also normalizes quoting
// Currently, it truncates any environment variables to a max of 1024 bytes
pub const NodeEnvBufMap = struct {
    backing: std.BufMap,
    pub fn init(allocator: *std.mem.Allocator) NodeEnvBufMap {
        return NodeEnvBufMap{ .backing = std.BufMap.init(allocator) };
    }
    pub fn get(this: *const NodeEnvBufMap, key: string) ?string {
        return this.backing.get(key);
    }
    pub threadlocal var bufkeybuf: [4096]u8 = undefined;
    pub threadlocal var bufkeybuf_first = true;

    pub fn iterator(this: *NodeEnvBufMap) @typeInfo(@TypeOf(std.BufMap.iterator)).Fn.return_type.? {
        return this.backing.iterator();
    }

    pub fn put(this: *NodeEnvBufMap, key: string, value: anytype) !void {
        if (value.len == 0) {
            return;
        }

        if (bufkeybuf_first) {
            std.mem.copy(u8, &bufkeybuf, "process.env.");
            bufkeybuf_first = false;
        }
        std.mem.copy(u8, bufkeybuf["process.env.".len..], key);
        var key_slice = bufkeybuf[0 .. key.len + "process.env.".len];
        var value_slice = value;
        if (value_slice.len > 0) {
            const max_value_slice_len = std.math.min(value.len, bufkeybuf.len - key_slice.len);
            if (key_slice.len < bufkeybuf.len and value_slice[0] != '"' and value_slice[value.len - 1] != '"') {
                value_slice = bufkeybuf[key_slice.len..];
                if (value_slice.len > 0) {
                    value_slice = value_slice[0 .. max_value_slice_len + 2];
                    value_slice[0] = '"';
                    std.mem.copy(u8, value_slice[1..], value[0..max_value_slice_len]);
                    value_slice[value_slice.len - 1] = '"';
                } else {
                    value_slice.len = 0;
                }
            } else if (value_slice[0] != '"') {
                value_slice[0] = '"';
                std.mem.copy(u8, value_slice[1..], value[0..max_value_slice_len]);
            } else if (value_slice[value.len - 1] != '"') {
                std.mem.copy(u8, value_slice[1..], value[0..max_value_slice_len]);
                value_slice[value_slice.len - 1] = '"';
            }
        }

        return this.backing.put(key_slice, value_slice);
    }

    pub fn count(this: *const NodeEnvBufMap) usize {
        return this.backing.count();
    }

    pub fn deinit(this: *NodeEnvBufMap) void {
        this.backing.deinit();
    }
};

pub fn getNodeEnvMap(allocator: *std.mem.Allocator) !NodeEnvBufMap {
    var result = NodeEnvBufMap.init(allocator);
    errdefer result.deinit();
    const builtin = std.builtin;
    if (builtin.os.tag == .windows) {
        const ptr = os.windows.peb().ProcessParameters.Environment;

        var i: usize = 0;
        while (ptr[i] != 0) {
            const key_start = i;

            while (ptr[i] != 0 and ptr[i] != '=') : (i += 1) {}
            const key_w = ptr[key_start..i];
            const key = try std.unicode.utf16leToUtf8Alloc(allocator, key_w);
            errdefer allocator.free(key);

            if (ptr[i] == '=') i += 1;

            const value_start = i;
            while (ptr[i] != 0) : (i += 1) {}
            const value_w = ptr[value_start..i];
            const value = try std.unicode.utf16leToUtf8Alloc(allocator, value_w);
            errdefer allocator.free(value);

            i += 1; // skip over null byte

            try result.putMove(key, value);
        }
        return result;
    } else if (builtin.os.tag == .wasi) {
        var environ_count: usize = undefined;
        var environ_buf_size: usize = undefined;

        const environ_sizes_get_ret = os.wasi.environ_sizes_get(&environ_count, &environ_buf_size);
        if (environ_sizes_get_ret != os.wasi.ESUCCESS) {
            return os.unexpectedErrno(environ_sizes_get_ret);
        }

        var environ = try allocator.alloc([*:0]u8, environ_count);
        defer allocator.free(environ);
        var environ_buf = try allocator.alloc(u8, environ_buf_size);
        defer allocator.free(environ_buf);

        const environ_get_ret = os.wasi.environ_get(environ.ptr, environ_buf.ptr);
        if (environ_get_ret != os.wasi.ESUCCESS) {
            return os.unexpectedErrno(environ_get_ret);
        }

        for (environ) |env| {
            const pair = mem.spanZ(env);
            var parts = mem.split(pair, "=");
            const key = parts.next().?;
            const value = parts.next().?;
            try result.put(key, value);
        }
        return result;
    } else if (builtin.link_libc) {
        var ptr = std.c.environ;
        while (ptr.*) |line| : (ptr += 1) {
            var line_i: usize = 0;
            while (line[line_i] != 0 and line[line_i] != '=') : (line_i += 1) {}
            const key = line[0..line_i];

            var end_i: usize = line_i;
            while (line[end_i] != 0) : (end_i += 1) {}
            const value = line[line_i + 1 .. end_i];

            try result.put(key, value);
        }
        return result;
    } else {
        for (os.environ) |line| {
            var line_i: usize = 0;
            while (line[line_i] != 0 and line[line_i] != '=') : (line_i += 1) {}
            const key = line[0..line_i];

            var end_i: usize = line_i;
            while (line[end_i] != 0) : (end_i += 1) {}
            const value = line[line_i + 1 .. end_i];

            try result.put(key, value);
        }
        return result;
    }
}