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. */
| 6144 | * installer script may update only its process-local PATH after this returns; |
| 6145 | * it must not perform a second persistent mutation outside coordination. */ |
| 6146 | static int cli_ensure_windows_user_path(const char *bin_dir, bool dry_run) { |
| 6147 | wchar_t *wide_dir = cli_windows_utf8_to_wide(bin_dir); |
| 6148 | HKEY environment = NULL; |
| 6149 | if (!wide_dir || cli_windows_open_user_path_key(&environment) != CLI_OK) { |
| 6150 | free(wide_dir); |
| 6151 | return CLI_ERR; |
| 6152 | } |
| 6153 | |
| 6154 | DWORD type = REG_EXPAND_SZ; |
| 6155 | DWORD bytes = 0; |
| 6156 | LONG queried = RegQueryValueExW(environment, L"Path", NULL, &type, NULL, &bytes); |
| 6157 | bool missing = queried == ERROR_FILE_NOT_FOUND; |
| 6158 | if ((!missing && queried != ERROR_SUCCESS) || |
| 6159 | (!missing && type != REG_SZ && type != REG_EXPAND_SZ)) { |
| 6160 | RegCloseKey(environment); |
| 6161 | free(wide_dir); |
| 6162 | return CLI_ERR; |
| 6163 | } |
| 6164 | size_t existing_capacity = missing ? 1U : (size_t)bytes / sizeof(wchar_t) + 1U; |
| 6165 | wchar_t *existing = calloc(existing_capacity, sizeof(*existing)); |
| 6166 | if (!existing) { |
| 6167 | RegCloseKey(environment); |
| 6168 | free(wide_dir); |
| 6169 | return CLI_ERR; |
| 6170 | } |
| 6171 | if (!missing) { |
| 6172 | DWORD read_bytes = bytes; |
| 6173 | if (RegQueryValueExW(environment, L"Path", NULL, &type, (BYTE *)existing, &read_bytes) != |
| 6174 | ERROR_SUCCESS) { |
| 6175 | RegCloseKey(environment); |
| 6176 | free(existing); |
| 6177 | free(wide_dir); |
| 6178 | return CLI_ERR; |
| 6179 | } |
| 6180 | existing[existing_capacity - 1U] = L'\0'; |
| 6181 | } |
| 6182 | |
| 6183 | bool present = false; |
| 6184 | const wchar_t *cursor = existing; |
| 6185 | while (!present && *cursor) { |
| 6186 | const wchar_t *separator = wcschr(cursor, L';'); |
| 6187 | size_t length = separator ? (size_t)(separator - cursor) : wcslen(cursor); |
| 6188 | present = cli_windows_path_segment_equal(cursor, length, wide_dir); |
| 6189 | cursor = separator ? separator + 1 : cursor + length; |
| 6190 | } |
| 6191 | if (present || dry_run) { |
| 6192 | RegCloseKey(environment); |
| 6193 | free(existing); |
| 6194 | free(wide_dir); |
| 6195 | return present ? CLI_TRUE : CLI_OK; |
| 6196 | } |
| 6197 | |
| 6198 | size_t existing_length = wcslen(existing); |
| 6199 | size_t directory_length = wcslen(wide_dir); |
| 6200 | bool separator_needed = existing_length > 0 && existing[existing_length - 1U] != L';'; |
| 6201 | if (existing_length > SIZE_MAX - directory_length - 2U) { |
| 6202 | RegCloseKey(environment); |
| 6203 | free(existing); |
no test coverage detected