Kylin/Universal/CommandLineInterpreter.cpp
2023-07-25 10:40:14 +08:00

102 lines
3.7 KiB
C++

#include "CommandLineInterpreter.h"
#include "BoostLog.h"
#include <boost/program_options/parsers.hpp>
#include <boost/program_options/variables_map.hpp>
#include <iostream>
CommandLineInterpreter::CommandLineInterpreter(const DescriptionPointer &description, const std::string &prompt)
: m_description(description), m_prompt(prompt) {}
void CommandLineInterpreter::interpret(std::istream &inputStream) {
std::string command;
std::cout << m_prompt << std::flush;
while (std::getline(inputStream, command, '\n')) {
handleReadLine(command);
std::cout << m_prompt << std::flush;
}
}
void CommandLineInterpreter::handleReadLine(std::string line) {
using namespace boost::program_options;
if (m_description.expired()) {
LOG(error) << "description has expired.";
return;
}
auto description = m_description.lock();
std::vector<std::string> args;
// huu, ugly...
args = splitCommandLine(std::string("--") + line);
try {
variables_map vm;
store(command_line_parser(args).options(*description).run(), vm);
notify(vm);
} catch (boost::program_options::unknown_option &e) {
std::cerr << "error: " << e.what() << std::endl;
} catch (boost::program_options::invalid_command_line_syntax &e) {
std::cerr << "error: " << e.what() << std::endl;
} catch (boost::program_options::validation_error &e) {
std::cerr << "error: " << e.what() << std::endl;
}
}
std::vector<std::string> CommandLineInterpreter::splitCommandLine(const std::string &input) {
std::vector<std::string> result;
std::string::const_iterator i = input.begin(), e = input.end();
for (; i != e; ++i)
if (!isspace((unsigned char)*i)) break;
if (i != e) {
std::string current;
bool inside_quoted = false;
int backslash_count = 0;
for (; i != e; ++i) {
if (*i == '"') {
// '"' preceded by even number (n) of backslashes generates
// n/2 backslashes and is a quoted block delimiter
if (backslash_count % 2 == 0) {
current.append(backslash_count / 2, '\\');
inside_quoted = !inside_quoted;
// '"' preceded by odd number (n) of backslashes generates
// (n-1)/2 backslashes and is literal quote.
} else {
current.append(backslash_count / 2, '\\');
current += '"';
}
backslash_count = 0;
} else if (*i == '\\') {
++backslash_count;
} else {
// Not quote or backslash. All accumulated backslashes should be
// added
if (backslash_count) {
current.append(backslash_count, '\\');
backslash_count = 0;
}
if (isspace((unsigned char)*i) && !inside_quoted) {
// Space outside quoted section terminate the current argument
result.push_back(current);
current.resize(0);
for (; i != e && isspace((unsigned char)*i); ++i)
;
--i;
} else {
current += *i;
}
}
}
// If we have trailing backslashes, add them
if (backslash_count) current.append(backslash_count, '\\');
// If we have non-empty 'current' or we're still in quoted
// section (even if 'current' is empty), add the last token.
if (!current.empty() || inside_quoted) result.push_back(current);
}
return result;
}