| 369 | } |
| 370 | |
| 371 | const char *cbm_safe_getenv(const char *name, char *buf, size_t buf_sz, const char *fallback) { |
| 372 | if (!name || !name[0] || !buf || buf_sz == 0) { |
| 373 | return NULL; |
| 374 | } |
| 375 | buf[0] = '\0'; |
| 376 | #ifdef _WIN32 |
| 377 | /* #996 Layer 2: _environ holds ANSI-code-page bytes, NOT UTF-8. A |
| 378 | * non-ASCII value (USERPROFILE of C:\Users\Kovács János, or a Greek/CJK |
| 379 | * CBM_CACHE_DIR) arrives here either mojibake'd or with unrepresentable |
| 380 | * characters replaced by '?', which is INVALID in Windows paths — every |
| 381 | * downstream wide-safe file API then fails no matter how correct it is. |
| 382 | * Read the value wide and convert to genuine UTF-8, matching the |
| 383 | * UTF-8-path convention the rest of the codebase (cbm_fopen, _wmkdir, |
| 384 | * SQLite VFS) already assumes. */ |
| 385 | { |
| 386 | wchar_t wname[CBM_SZ_256]; |
| 387 | int wn = MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, name, -1, wname, CBM_SZ_256); |
| 388 | if (wn > 0) { |
| 389 | SetLastError(ERROR_SUCCESS); |
| 390 | DWORD needed = GetEnvironmentVariableW(wname, NULL, 0U); |
| 391 | DWORD environment_error = GetLastError(); |
| 392 | if (needed == 0U) { |
| 393 | if (environment_error == ERROR_ENVVAR_NOT_FOUND) { |
| 394 | return fallback ? platform_copy_environment_value(buf, buf_sz, fallback) : NULL; |
| 395 | } |
| 396 | /* An existing empty variable is distinct from a missing one. */ |
| 397 | return buf; |
| 398 | } |
| 399 | wchar_t *wval = calloc((size_t)needed, sizeof(*wval)); |
| 400 | if (!wval) { |
| 401 | return NULL; |
| 402 | } |
| 403 | SetLastError(ERROR_SUCCESS); |
| 404 | DWORD got = GetEnvironmentVariableW(wname, wval, needed); |
| 405 | DWORD read_error = GetLastError(); |
| 406 | if (got >= needed || (got == 0U && read_error != ERROR_SUCCESS)) { |
| 407 | free(wval); |
| 408 | return NULL; |
| 409 | } |
| 410 | char *utf8 = cbm_wide_to_utf8(wval); |
| 411 | free(wval); |
| 412 | if (!utf8) { |
| 413 | return NULL; |
| 414 | } |
| 415 | const char *copied = platform_copy_environment_value(buf, buf_sz, utf8); |
| 416 | free(utf8); |
| 417 | return copied; |
| 418 | } |
| 419 | return NULL; |
| 420 | } |
| 421 | #else |
| 422 | char **env = CBM_ENVIRON; |
| 423 | if (env) { |
| 424 | size_t nlen = strlen(name); |
| 425 | for (; *env; env++) { |
| 426 | if (strncmp(*env, name, nlen) == 0 && (*env)[nlen] == '=') { |
| 427 | return platform_copy_environment_value(buf, buf_sz, *env + nlen + SKIP_ONE); |
| 428 | } |