| 133 | /* ── setenv / unsetenv (Windows lacks them) ──────────────────── */ |
| 134 | #ifdef _WIN32 |
| 135 | static inline int cbm_setenv(const char *name, const char *value, int overwrite) { |
| 136 | (void)overwrite; |
| 137 | if (!name || !value) { |
| 138 | return EINVAL; |
| 139 | } |
| 140 | int name_chars = MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, name, -1, NULL, 0); |
| 141 | int value_chars = MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, value, -1, NULL, 0); |
| 142 | wchar_t *wide_name = |
| 143 | name_chars > 0 ? (wchar_t *)malloc((size_t)name_chars * sizeof(*wide_name)) : NULL; |
| 144 | wchar_t *wide_value = |
| 145 | value_chars > 0 ? (wchar_t *)malloc((size_t)value_chars * sizeof(*wide_value)) : NULL; |
| 146 | bool converted = |
| 147 | wide_name && wide_value && |
| 148 | MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, name, -1, wide_name, name_chars) > 0 && |
| 149 | MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, value, -1, wide_value, value_chars) > 0; |
| 150 | if (!converted) { |
| 151 | free(wide_name); |
| 152 | free(wide_value); |
| 153 | return EINVAL; |
| 154 | } |
| 155 | /* Keep the CRT's narrow environment useful for legacy getenv callers, |
| 156 | * then repair the process-wide Windows environment with the actual UTF-16 |
| 157 | * value. _putenv_s alone routes UTF-8 path bytes through the active ANSI |
| 158 | * code page, which corrupts non-ASCII cache roots inherited by children. */ |
| 159 | int status = _putenv_s(name, value); |
| 160 | if (status == 0 && !SetEnvironmentVariableW(wide_name, wide_value)) { |
| 161 | status = EINVAL; |
| 162 | } |
| 163 | free(wide_name); |
| 164 | free(wide_value); |
| 165 | return status; |
| 166 | } |
| 167 | static inline int cbm_unsetenv(const char *name) { |
| 168 | if (!name) { |
| 169 | return EINVAL; |
no outgoing calls