-
Notifications
You must be signed in to change notification settings - Fork 5
fest: Add resolver to win #64
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Seele-Official
merged 4 commits into
clice-io:main
from
Seele-Official:add-resolver-to-win
Mar 23, 2026
Merged
Changes from all commits
Commits
Show all changes
4 commits
Select commit
Hold shift + click to select a range
5c28287
feat: implement path resolution and environment variable retrieval in…
Seele-Official db15ba4
Refactor catter-hook payload: Enhance resolver and utility functions
Seele-Official d34ac63
refactor: streamline resolver functions and improve code organization
Seele-Official 717ba75
refactor: simplify resolver logic and enhance search path functionality
Seele-Official File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,2 @@ | ||
| LIBRARY catter-hook-win64 | ||
| EXPORTS |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,177 @@ | ||
| #include "resolver.h" | ||
|
|
||
| namespace catter::win::payload { | ||
| namespace detail { | ||
|
|
||
| template <CharT char_t> | ||
| constexpr char_t k_path_delimiter = char_t(';'); | ||
|
|
||
| template <CharT char_t> | ||
| constexpr char_t k_path_sep = char_t('\\'); | ||
|
|
||
| template <CharT char_t> | ||
| std::vector<std::basic_string<char_t>> split_path_var(std::basic_string_view<char_t> value) { | ||
| std::vector<std::basic_string<char_t>> segments; | ||
| size_t start = 0; | ||
| while(start <= value.size()) { | ||
| auto split_pos = value.find(k_path_delimiter<char_t>, start); | ||
| auto token = | ||
| value.substr(start, | ||
| split_pos == std::basic_string_view<char_t>::npos ? value.size() - start | ||
| : split_pos - start); | ||
| if(!token.empty()) { | ||
| segments.emplace_back(token); | ||
| } | ||
|
|
||
| if(split_pos == std::basic_string_view<char_t>::npos) { | ||
| break; | ||
| } | ||
| start = split_pos + 1; | ||
| } | ||
| return segments; | ||
| } | ||
|
|
||
| template <CharT char_t> | ||
| void push_if_non_empty(std::vector<std::basic_string<char_t>>& out, | ||
| std::basic_string<char_t> value) { | ||
| if(!value.empty()) { | ||
| out.push_back(std::move(value)); | ||
| } | ||
| } | ||
|
|
||
| template <CharT char_t> | ||
| std::basic_string<char_t> get_current_drive_root() { | ||
| auto cwd = GetCurrentDirectoryDynamic<char_t>(); | ||
| if(cwd.size() < 2 || cwd[1] != char_t(':')) { | ||
| return {}; | ||
| } | ||
|
|
||
| std::basic_string<char_t> root; | ||
| root.push_back(cwd[0]); | ||
| root.push_back(char_t(':')); | ||
| root.push_back(k_path_sep<char_t>); | ||
| return root; | ||
| } | ||
|
|
||
| template <CharT char_t> | ||
| constexpr std::basic_string_view<char_t> k_path_env_name = {}; | ||
|
|
||
| template <> | ||
| constexpr std::basic_string_view<char> k_path_env_name<char> = "PATH"; | ||
|
|
||
| template <> | ||
| constexpr std::basic_string_view<wchar_t> k_path_env_name<wchar_t> = L"PATH"; | ||
|
|
||
| template <CharT char_t> | ||
| constexpr std::basic_string_view<char_t> k_system16_name = {}; | ||
|
|
||
| template <> | ||
| constexpr std::basic_string_view<char> k_system16_name<char> = "System"; | ||
|
|
||
| template <> | ||
| constexpr std::basic_string_view<wchar_t> k_system16_name<wchar_t> = L"System"; | ||
|
|
||
| template <CharT char_t> | ||
| constexpr std::basic_string_view<char_t> exe_suffix; | ||
| template <> | ||
| constexpr std::basic_string_view<char> exe_suffix<char> = ".exe"; | ||
| template <> | ||
| constexpr std::basic_string_view<wchar_t> exe_suffix<wchar_t> = L".exe"; | ||
| } // namespace detail | ||
|
|
||
| template <CharT char_t> | ||
| std::basic_string<char_t> Resolver<char_t>::resolve(std::basic_string_view<char_t> app_name) const { | ||
| if(app_name.empty()) { | ||
| return {}; | ||
| } | ||
|
|
||
| auto original_name = std::basic_string<char_t>(app_name); | ||
|
|
||
| auto search_paths = join_search_paths(m_search_paths); | ||
| auto resolved = SearchPathDynamic<char_t>(search_paths.empty() ? nullptr : search_paths.c_str(), | ||
| app_name, | ||
| detail::exe_suffix<char_t>.data()); | ||
| if(!resolved.empty()) { | ||
| return resolved; | ||
| } | ||
|
|
||
| // Keep original input when search paths do not resolve an existing file. | ||
| return original_name; | ||
| } | ||
|
|
||
| template <CharT char_t> | ||
| std::basic_string<char_t> Resolver<char_t>::join_search_paths( | ||
| const std::vector<std::basic_string<char_t>>& search_paths) { | ||
| std::basic_string<char_t> merged; | ||
| for(const auto& path: search_paths) { | ||
| if(path.empty()) { | ||
| continue; | ||
| } | ||
| if(!merged.empty()) { | ||
| merged.push_back(char_t(';')); | ||
| } | ||
| merged.append(path); | ||
| } | ||
| return merged; | ||
| } | ||
|
|
||
| template <CharT char_t> | ||
| Resolver<char_t> create_app_name_resolver() { | ||
| std::vector<std::basic_string<char_t>> search_paths; | ||
| search_paths.reserve(2); | ||
| // Search order for explicit application_name: | ||
| // 1) current drive root, 2) current directory. | ||
| detail::push_if_non_empty(search_paths, detail::get_current_drive_root<char_t>()); | ||
| detail::push_if_non_empty(search_paths, GetCurrentDirectoryDynamic<char_t>()); | ||
| return Resolver<char_t>(std::move(search_paths)); | ||
| } | ||
|
|
||
| template <CharT char_t> | ||
| Resolver<char_t> create_command_line_resolver() { | ||
| std::vector<std::basic_string<char_t>> search_paths; | ||
| search_paths.reserve(64); | ||
| // Search order for command line token: | ||
| // 1) directory of current process image. | ||
| // 2) current directory. | ||
| // 3) 32-bit system directory. | ||
| // 4) 16-bit system directory named "System" under Windows directory. | ||
| // 5) Windows directory. | ||
| // 6) directories listed in PATH. | ||
| auto module_dir = GetModuleDirectory<char_t>(nullptr); | ||
| auto current_dir = GetCurrentDirectoryDynamic<char_t>(); | ||
| auto system_dir = GetSystemDirectoryDynamic<char_t>(); | ||
| auto windows_dir = GetWindowsDirectoryDynamic<char_t>(); | ||
|
|
||
| detail::push_if_non_empty(search_paths, std::move(module_dir)); | ||
| detail::push_if_non_empty(search_paths, std::move(current_dir)); | ||
| detail::push_if_non_empty(search_paths, std::move(system_dir)); | ||
| if(!windows_dir.empty()) { | ||
| auto system16_dir = windows_dir; | ||
| if(system16_dir.back() != detail::k_path_sep<char_t> && | ||
| system16_dir.back() != char_t('/')) { | ||
| system16_dir.push_back(detail::k_path_sep<char_t>); | ||
| } | ||
| system16_dir.append(detail::k_system16_name<char_t>); | ||
| detail::push_if_non_empty(search_paths, std::move(system16_dir)); | ||
| } | ||
| detail::push_if_non_empty(search_paths, windows_dir); | ||
|
|
||
| auto path_value = | ||
| GetEnvironmentVariableDynamic<char_t>(detail::k_path_env_name<char_t>.data(), 4096); | ||
| auto path_segments = detail::split_path_var<char_t>(path_value); | ||
| for(auto& segment: path_segments) { | ||
| detail::push_if_non_empty(search_paths, std::move(segment)); | ||
| } | ||
|
|
||
| return Resolver<char_t>(std::move(search_paths)); | ||
| } | ||
|
|
||
| template class Resolver<char>; | ||
| template class Resolver<wchar_t>; | ||
|
|
||
| template Resolver<char> create_app_name_resolver(); | ||
| template Resolver<wchar_t> create_app_name_resolver(); | ||
| template Resolver<char> create_command_line_resolver(); | ||
| template Resolver<wchar_t> create_command_line_resolver(); | ||
|
|
||
| } // namespace catter::win::payload |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,37 @@ | ||
| #pragma once | ||
|
|
||
| #include <algorithm> | ||
| #include <string> | ||
| #include <string_view> | ||
| #include <type_traits> | ||
| #include <utility> | ||
| #include <vector> | ||
|
|
||
| #include "winapi.h" | ||
|
|
||
| namespace catter::win::payload { | ||
|
|
||
| template <CharT char_t> | ||
| class Resolver { | ||
| public: | ||
| explicit Resolver(std::vector<std::basic_string<char_t>> search_paths) : | ||
| m_search_paths(std::move(search_paths)) {} | ||
|
|
||
| std::basic_string<char_t> resolve(std::basic_string_view<char_t> app_name) const; | ||
|
|
||
| private: | ||
| static std::basic_string<char_t> | ||
| join_search_paths(const std::vector<std::basic_string<char_t>>& search_paths); | ||
| std::vector<std::basic_string<char_t>> m_search_paths; | ||
| }; | ||
|
|
||
| template <CharT char_t> | ||
| Resolver<char_t> create_app_name_resolver(); | ||
|
|
||
| template <CharT char_t> | ||
| Resolver<char_t> create_command_line_resolver(); | ||
|
|
||
| extern template class Resolver<char>; | ||
| extern template class Resolver<wchar_t>; | ||
|
|
||
| } // namespace catter::win::payload |
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.