// Copyright (c) 2017-2025, University of Cincinnati, developed by Henry Schreiner // under NSF AWARD 1414736 and by the respective contributors. // All rights reserved. // // SPDX-License-Identifier: BSD-3-Clause // Code inspired by discussion from https://github.com/CLIUtils/CLI11/issues/1149 #include #include #include #include #include // Levenshtein distance function code generated by chatgpt int levenshteinDistance(const std::string& s1, const std::string& s2) { size_t len1 = s1.size(), len2 = s2.size(); std::vector> dp(len1 + 1, std::vector(len2 + 1)); for (size_t i = 0; i <= len1; ++i) dp[i][0] = i; for (size_t j = 0; j <= len2; ++j) dp[0][j] = j; for (size_t i = 1; i <= len1; ++i) { for (size_t j = 1; j <= len2; ++j) { int cost = (s1[i - 1] == s2[j - 1]) ? 0 : 1; dp[i][j] = std::min({ dp[i - 1][j] + 1, // deletion dp[i][j - 1] + 1, // insertion dp[i - 1][j - 1] + cost // substitution }); } } return dp[len1][len2]; } // Finds the closest string from a list std::pair findClosestMatch(const std::string& input, const std::vector& candidates) { std::string closest; int minDistance = std::numeric_limits::max(); for (const auto& candidate : candidates) { int distance = levenshteinDistance(input, candidate); if (distance < minDistance) { minDistance = distance; closest = candidate; } } return { closest,minDistance }; } /** This example demonstrates the use of `prefix_command` on a subcommand to capture all subsequent arguments along with an alias to make it appear as a regular options. All the values after the "sub" or "--sub" are available in the remaining() method. */ int main(int argc, const char *argv[]) { int value{0}; CLI::App app{"cose string App"}; app.add_option("-v", value, "value"); app.add_subcommand("install", ""); app.add_subcommand("upgrade",""); app.add_subcommand("remove",""); app.add_subcommand("test",""); app.allow_extras(true); app.parse_complete_callback([&app]() { auto extras = app.remaining(); if (extras.empty()) { return; } auto subs = app.get_subcommands(nullptr); std::vector list; for (const auto* sub : subs) { if (!sub->get_name().empty()) { list.push_back(sub->get_name()); } auto aliases = sub->get_aliases(); if (!aliases.empty()) { list.insert(list.end(), aliases.begin(), aliases.end()); } } for (auto& extra : extras) { if (extra.front() != '-') { auto closest = findClosestMatch(extra,list); if (closest.second <= 3) { std::cout<<"unmatched commands "<