src: Add command-line option guess

This commit is contained in:
Tatsuhiro Tsujikawa 2014-01-09 01:27:56 +09:00
parent a15fc5fbb4
commit 90ea7ba92a
5 changed files with 84 additions and 0 deletions

View File

@ -1801,6 +1801,7 @@ int main(int argc, char **argv)
}
break;
case '?':
util::show_candidates(argv[optind - 1], long_options);
exit(EXIT_FAILURE);
case 0:
switch(flag) {

View File

@ -41,6 +41,7 @@
#include "app_helper.h"
#include "HttpServer.h"
#include "util.h"
namespace nghttp2 {
@ -178,6 +179,7 @@ int main(int argc, char **argv)
}
break;
case '?':
util::show_candidates(argv[optind - 1], long_options);
exit(EXIT_FAILURE);
case 0:
switch(flag) {

View File

@ -52,6 +52,9 @@
#include "shrpx_config.h"
#include "shrpx_listen_handler.h"
#include "shrpx_ssl.h"
#include "util.h"
using namespace nghttp2;
namespace shrpx {
@ -859,6 +862,7 @@ int main(int argc, char **argv)
print_version(std::cout);
exit(EXIT_SUCCESS);
case '?':
util::show_candidates(argv[optind - 1], long_options);
exit(EXIT_FAILURE);
case 0:
switch(flag) {

View File

@ -29,6 +29,7 @@
#include <cassert>
#include <cstdio>
#include <cstring>
#include <iostream>
#include "timegm.h"
@ -281,6 +282,77 @@ void inp_strlower(std::string& s)
}
}
namespace {
// Calculates DamerauLevenshtein distance between c-string a and b
// with given costs. swapcost, subcost, addcost and delcost are cost
// to swap 2 adjacent characters, substitute characters, add character
// and delete character respectively.
int levenshtein
(const char* a,
const char* b,
int swapcost,
int subcost,
int addcost,
int delcost)
{
int alen = strlen(a);
int blen = strlen(b);
auto dp = std::vector<std::vector<int>>(3, std::vector<int>(blen+1));
for(int i = 0; i <= blen; ++i) {
dp[1][i] = i;
}
for(int i = 1; i <= alen; ++i) {
dp[0][0] = i;
for(int j = 1; j <= blen; ++j) {
dp[0][j] = dp[1][j-1]+(a[i-1] == b[j-1] ? 0 : subcost);
if(i >= 2 && j >= 2 && a[i-1] != b[j-1] &&
a[i-2] == b[j-1] && a[i-1] == b[j-2]) {
dp[0][j] = std::min(dp[0][j], dp[2][j-2]+swapcost);
}
dp[0][j] = std::min(dp[0][j],
std::min(dp[1][j]+delcost, dp[0][j-1]+addcost));
}
std::rotate(std::begin(dp), std::begin(dp)+2, std::end(dp));
}
return dp[1][blen];
}
} // namespace
void show_candidates(const char *unkopt, option *options)
{
for(; *unkopt == '-'; ++unkopt);
if(*unkopt == '\0') {
return;
}
auto cands = std::vector<std::pair<int, const char*>>();
for(size_t i = 0; options[i].name != nullptr; ++i) {
// Use cost 0 for prefix match
if(istartsWith(options[i].name, unkopt)) {
cands.emplace_back(0, options[i].name);
continue;
}
// cost values are borrowed from git, help.c.
int sim = levenshtein(unkopt, options[i].name, 0, 2, 1, 3);
cands.emplace_back(sim, options[i].name);
}
if(cands.empty()) {
return;
}
std::sort(std::begin(cands), std::end(cands));
int threshold = cands[0].first;
// threshold value is a magic value.
if(threshold > 6) {
return;
}
std::cerr << "\nDid you mean:\n";
for(auto& item : cands) {
if(item.first > threshold) {
break;
}
std::cerr << "\t--" << item.second << "\n";
}
}
} // namespace util
} // namespace nghttp2

View File

@ -27,6 +27,9 @@
#include "nghttp2_config.h"
#include <unistd.h>
#include <getopt.h>
#include <cstring>
#include <vector>
#include <string>
@ -403,6 +406,8 @@ make_unique(size_t size)
void to_token68(std::string& base64str);
void to_base64(std::string& token68str);
void show_candidates(const char *unkopt, option *options);
} // namespace util
} // namespace nghttp2