Persist the current-user PATH while the activation lease is held. The * installer script may update only its process-local PATH after this returns; * it must not perform a second persistent mutation outside coordination. */
| 6001 | * installer script may update only its process-local PATH after this returns; |
| 6002 | * it must not perform a second persistent mutation outside coordination. */ |
| 6003 | static int cli_ensure_windows_user_path(const char *bin_dir, bool dry_run) { |
| 6004 | wchar_t *wide_dir = cli_windows_utf8_to_wide(bin_dir); |
| 6005 | HKEY environment = NULL; |
| 6006 | if (!wide_dir || cli_windows_open_user_path_key(&environment) != CLI_OK) { |
| 6007 | free(wide_dir); |
| 6008 | return CLI_ERR; |
| 6009 | } |
| 6010 | |
| 6011 | DWORD type = REG_EXPAND_SZ; |
| 6012 | DWORD bytes = 0; |
| 6013 | LONG queried = RegQueryValueExW(environment, L"Path", NULL, &type, NULL, &bytes); |
| 6014 | bool missing = queried == ERROR_FILE_NOT_FOUND; |
| 6015 | if ((!missing && queried != ERROR_SUCCESS) || |
| 6016 | (!missing && type != REG_SZ && type != REG_EXPAND_SZ)) { |
| 6017 | RegCloseKey(environment); |
| 6018 | free(wide_dir); |
| 6019 | return CLI_ERR; |
| 6020 | } |
| 6021 | size_t existing_capacity = missing ? 1U : (size_t)bytes / sizeof(wchar_t) + 1U; |
| 6022 | wchar_t *existing = calloc(existing_capacity, sizeof(*existing)); |
| 6023 | if (!existing) { |
| 6024 | RegCloseKey(environment); |
| 6025 | free(wide_dir); |
| 6026 | return CLI_ERR; |
| 6027 | } |
| 6028 | if (!missing) { |
| 6029 | DWORD read_bytes = bytes; |
| 6030 | if (RegQueryValueExW(environment, L"Path", NULL, &type, (BYTE *)existing, &read_bytes) != |
| 6031 | ERROR_SUCCESS) { |
| 6032 | RegCloseKey(environment); |
| 6033 | free(existing); |
| 6034 | free(wide_dir); |
| 6035 | return CLI_ERR; |
| 6036 | } |
| 6037 | existing[existing_capacity - 1U] = L'\0'; |
| 6038 | } |
| 6039 | |
| 6040 | bool present = false; |
| 6041 | const wchar_t *cursor = existing; |
| 6042 | while (!present && *cursor) { |
| 6043 | const wchar_t *separator = wcschr(cursor, L';'); |
| 6044 | size_t length = separator ? (size_t)(separator - cursor) : wcslen(cursor); |
| 6045 | present = cli_windows_path_segment_equal(cursor, length, wide_dir); |
| 6046 | cursor = separator ? separator + 1 : cursor + length; |
| 6047 | } |
| 6048 | if (present || dry_run) { |
| 6049 | RegCloseKey(environment); |
| 6050 | free(existing); |
| 6051 | free(wide_dir); |
| 6052 | return present ? CLI_TRUE : CLI_OK; |
| 6053 | } |
| 6054 | |
| 6055 | size_t existing_length = wcslen(existing); |
| 6056 | size_t directory_length = wcslen(wide_dir); |
| 6057 | bool separator_needed = existing_length > 0 && existing[existing_length - 1U] != L';'; |
| 6058 | if (existing_length > SIZE_MAX - directory_length - 2U) { |
| 6059 | RegCloseKey(environment); |
| 6060 | free(existing); |
no test coverage detected