// 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 #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] = static_cast(i); for(size_t j = 0; j <= len2; ++j) dp[0][j] = static_cast(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 (modified from chat gpt code) 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 " << extra << ", closest match is " << closest.first << "\n"; } } } }); CLI11_PARSE(app, argc, argv); return 0; }