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. */
| 5980 | * installer script may update only its process-local PATH after this returns; |
| 5981 | * it must not perform a second persistent mutation outside coordination. */ |
| 5982 | static int cli_ensure_windows_user_path(const char *bin_dir, bool dry_run) { |
| 5983 | wchar_t *wide_dir = cli_windows_utf8_to_wide(bin_dir); |
| 5984 | HKEY environment = NULL; |
| 5985 | if (!wide_dir || cli_windows_open_user_path_key(&environment) != CLI_OK) { |
| 5986 | free(wide_dir); |
| 5987 | return CLI_ERR; |
| 5988 | } |
| 5989 | |
| 5990 | DWORD type = REG_EXPAND_SZ; |
| 5991 | DWORD bytes = 0; |
| 5992 | LONG queried = RegQueryValueExW(environment, L"Path", NULL, &type, NULL, &bytes); |
| 5993 | bool missing = queried == ERROR_FILE_NOT_FOUND; |
| 5994 | if ((!missing && queried != ERROR_SUCCESS) || |
| 5995 | (!missing && type != REG_SZ && type != REG_EXPAND_SZ)) { |
| 5996 | RegCloseKey(environment); |
| 5997 | free(wide_dir); |
| 5998 | return CLI_ERR; |
| 5999 | } |
| 6000 | size_t existing_capacity = missing ? 1U : (size_t)bytes / sizeof(wchar_t) + 1U; |
| 6001 | wchar_t *existing = calloc(existing_capacity, sizeof(*existing)); |
| 6002 | if (!existing) { |
| 6003 | RegCloseKey(environment); |
| 6004 | free(wide_dir); |
| 6005 | return CLI_ERR; |
| 6006 | } |
| 6007 | if (!missing) { |
| 6008 | DWORD read_bytes = bytes; |
| 6009 | if (RegQueryValueExW(environment, L"Path", NULL, &type, (BYTE *)existing, &read_bytes) != |
| 6010 | ERROR_SUCCESS) { |
| 6011 | RegCloseKey(environment); |
| 6012 | free(existing); |
| 6013 | free(wide_dir); |
| 6014 | return CLI_ERR; |
| 6015 | } |
| 6016 | existing[existing_capacity - 1U] = L'\0'; |
| 6017 | } |
| 6018 | |
| 6019 | bool present = false; |
| 6020 | const wchar_t *cursor = existing; |
| 6021 | while (!present && *cursor) { |
| 6022 | const wchar_t *separator = wcschr(cursor, L';'); |
| 6023 | size_t length = separator ? (size_t)(separator - cursor) : wcslen(cursor); |
| 6024 | present = cli_windows_path_segment_equal(cursor, length, wide_dir); |
| 6025 | cursor = separator ? separator + 1 : cursor + length; |
| 6026 | } |
| 6027 | if (present || dry_run) { |
| 6028 | RegCloseKey(environment); |
| 6029 | free(existing); |
| 6030 | free(wide_dir); |
| 6031 | return present ? CLI_TRUE : CLI_OK; |
| 6032 | } |
| 6033 | |
| 6034 | size_t existing_length = wcslen(existing); |
| 6035 | size_t directory_length = wcslen(wide_dir); |
| 6036 | bool separator_needed = existing_length > 0 && existing[existing_length - 1U] != L';'; |
| 6037 | if (existing_length > SIZE_MAX - directory_length - 2U) { |
| 6038 | RegCloseKey(environment); |
| 6039 | free(existing); |
no test coverage detected