12 using std::vector, std::string, std::map;
13 namespace fs = std::filesystem;
18 fs::create_directories(path.parent_path());
19 std::ofstream out(path);
20 if (!out)
return false;
29 const char* shell = std::getenv(
"SHELL");
30 if (!shell)
return "";
33 if (s.find(
"bash") != std::string::npos)
return "bash";
34 if (s.find(
"zsh") != std::string::npos)
return "zsh";
40 const char* home = std::getenv(
"HOME");
42 std::cerr <<
"Cannot determine HOME directory\n";
48 std::cerr <<
"Could not detect your shell. Supported: bash, zsh\n";
54 std::string binary_path =
"mach3";
56 binary_path = fs::read_symlink(
"/proc/self/exe").string();
61 auto embed_path = [&](std::string_view tmpl) -> std::string {
63 const std::string placeholder =
"mach3 --complete";
64 const std::string replacement = binary_path +
" --complete";
65 for (std::size_t pos = s.find(placeholder); pos != std::string::npos;
66 pos = s.find(placeholder, pos + replacement.size()))
67 s.replace(pos, placeholder.size(), replacement);
71 fs::path home_path(home);
73 if (shell ==
"bash") {
74 fs::path path = home_path /
".local/share/bash-completion/completions/mach3";
76 std::cout <<
"Installed bash completions → " << path <<
"\n";
81 fs::path path = home_path /
".local/share/zsh/site-functions/_mach3";
83 std::cout <<
"Installed zsh completions → " << path <<
"\n";
90 std::string active_subcommand;
91 for (
const auto& word : context) {
94 active_subcommand = sub;
98 if (!active_subcommand.empty())
break;
101 if (active_subcommand.empty()) {
104 if (cmd.rfind(prefix, 0) == 0)
105 std::cout << cmd <<
"\n";
113 if (parser->name() == active_subcommand) { sub_parser = parser;
break; }
117 if (parser->name() == active_subcommand) { sub_parser = parser;
break; }
120 if (!sub_parser)
return;
124 if (!context.empty()) {
125 const std::string& prev = context.back();
126 if (prev.size() >= 2 && prev[0] ==
'-') {
134 if (!prefix.empty() && prefix[0] ==
'-') {
136 if (
name.rfind(prefix, 0) == 0)
137 std::cout <<
name <<
"\n";
173 this->add_subparser(*parser);
184 const char* env = std::getenv(
"MACH3_PLUGINS");
190 std::stringstream ss(env);
193 while (std::getline(ss, path,
':')) {
196 std::unique_ptr<DynamicPlugin> dlplugin = std::make_unique<DynamicPlugin>(sofile);
199 std::cerr <<
"Plugin " << sofile <<
" did not provide a valid parser.\n";
203 this->add_subparser(*parser);
207 catch (
const std::exception& e) {
208 std::cerr <<
"Failed to load plugin from " << sofile <<
": " << e.what() <<
"\n";
212 std::cerr <<
"Failed to load plugin from " << sofile <<
": unknown error\n";
237 return plugin_itr->second->Run();
241 return dplugin_itr->second->Run();
256 std::vector<std::string> result;
260 if (!fs::exists(p)) {
261 std::cerr <<
"Plugin path does not exist: " << path <<
"\n";
265 if (fs::is_regular_file(p)) {
267 result.push_back(path);
269 else if (fs::is_directory(p)) {
271 for (
auto& entry : fs::directory_iterator(p)) {
272 if (entry.is_regular_file() && entry.path().extension() ==
".so") {
273 result.push_back(entry.path().string());
278 std::cerr <<
"Invalid plugin path (not file or directory): " << path <<
"\n";
MaCh3 Logging utilities built on top of SPDLOG.
Core program class for the MaCh3 command-line interface.
Interface for MaCh3 plugins and modules.
virtual MaCh3ArgumentParser * get_parser()=0
Get the argument parser for this plugin.
Extended ArgumentParser with MaCh3-specific functionality.
const std::string name() const
Get the name of this parser/subcommand.
std::vector< std::string > get_long_option_names() const
Return all long option names (–xxx) registered with this parser.
bool option_takes_value(const std::string &name) const
Return true if the named option expects a value (not a flag)
const MaCh3ArgumentParser & get_subcommand_used() const
Get the subcommand that was used in parsing.
void add_core_module(IModule &module)
Add a core module to the program.
void unload_dynamic_plugins()
Unload all dynamically loaded plugins.
bool write_file(const fs::path &path, std::string_view content) const
Write content to a file, creating parent directories if needed.
static constexpr std::string_view ZSH_COMPLETION
std::vector< std::string > m_subcommands
List of registered subcommand names.
void completions(const std::string &prefix, const std::vector< std::string > &context) const
Generate completion suggestions given the current prefix and preceding words.
void install_completions() const
Install shell completion scripts for bash and zsh.
static constexpr std::string_view BASH_COMPLETION
int Run()
Run the selected subcommand.
std::map< const MaCh3ArgumentParser *, IModule * > m_module_map
Map of parsers to core modules (non-owning)
std::map< const MaCh3ArgumentParser *, std::unique_ptr< DynamicPlugin > > m_dynamic_plugin_map
Map of parsers to dynamic plugins (owning)
std::string detect_shell() const
Detect the user's shell from the SHELL environment variable.
void load_dynamic_plugins()
Load dynamic plugins from the MACH3_PLUGINS environment variable.
std::vector< std::string > expand_plugin_path(const std::string &path) const
Expand a plugin path (file or directory) into a list of .so files.
Main namespace for MaCh3 software.