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. */
| 5924 | * installer script may update only its process-local PATH after this returns; |
| 5925 | * it must not perform a second persistent mutation outside coordination. */ |
| 5926 | static int cli_ensure_windows_user_path(const char *bin_dir, bool dry_run) { |
| 5927 | wchar_t *wide_dir = cli_windows_utf8_to_wide(bin_dir); |
| 5928 | HKEY environment = NULL; |
| 5929 | if (!wide_dir || cli_windows_open_user_path_key(&environment) != CLI_OK) { |
| 5930 | free(wide_dir); |
| 5931 | return CLI_ERR; |
| 5932 | } |
| 5933 | |
| 5934 | DWORD type = REG_EXPAND_SZ; |
| 5935 | DWORD bytes = 0; |
| 5936 | LONG queried = RegQueryValueExW(environment, L"Path", NULL, &type, NULL, &bytes); |
| 5937 | bool missing = queried == ERROR_FILE_NOT_FOUND; |
| 5938 | if ((!missing && queried != ERROR_SUCCESS) || |
| 5939 | (!missing && type != REG_SZ && type != REG_EXPAND_SZ)) { |
| 5940 | RegCloseKey(environment); |
| 5941 | free(wide_dir); |
| 5942 | return CLI_ERR; |
| 5943 | } |
| 5944 | size_t existing_capacity = missing ? 1U : (size_t)bytes / sizeof(wchar_t) + 1U; |
| 5945 | wchar_t *existing = calloc(existing_capacity, sizeof(*existing)); |
| 5946 | if (!existing) { |
| 5947 | RegCloseKey(environment); |
| 5948 | free(wide_dir); |
| 5949 | return CLI_ERR; |
| 5950 | } |
| 5951 | if (!missing) { |
| 5952 | DWORD read_bytes = bytes; |
| 5953 | if (RegQueryValueExW(environment, L"Path", NULL, &type, (BYTE *)existing, &read_bytes) != |
| 5954 | ERROR_SUCCESS) { |
| 5955 | RegCloseKey(environment); |
| 5956 | free(existing); |
| 5957 | free(wide_dir); |
| 5958 | return CLI_ERR; |
| 5959 | } |
| 5960 | existing[existing_capacity - 1U] = L'\0'; |
| 5961 | } |
| 5962 | |
| 5963 | bool present = false; |
| 5964 | const wchar_t *cursor = existing; |
| 5965 | while (!present && *cursor) { |
| 5966 | const wchar_t *separator = wcschr(cursor, L';'); |
| 5967 | size_t length = separator ? (size_t)(separator - cursor) : wcslen(cursor); |
| 5968 | present = cli_windows_path_segment_equal(cursor, length, wide_dir); |
| 5969 | cursor = separator ? separator + 1 : cursor + length; |
| 5970 | } |
| 5971 | if (present || dry_run) { |
| 5972 | RegCloseKey(environment); |
| 5973 | free(existing); |
| 5974 | free(wide_dir); |
| 5975 | return present ? CLI_TRUE : CLI_OK; |
| 5976 | } |
| 5977 | |
| 5978 | size_t existing_length = wcslen(existing); |
| 5979 | size_t directory_length = wcslen(wide_dir); |
| 5980 | bool separator_needed = existing_length > 0 && existing[existing_length - 1U] != L';'; |
| 5981 | if (existing_length > SIZE_MAX - directory_length - 2U) { |
| 5982 | RegCloseKey(environment); |
| 5983 | free(existing); |
no test coverage detected