35 using std::string_view;
51 , cassettePlayerCLI(*this)
53 , laserdiscPlayerCLI(*this)
58 , parseStatus(UNPARSED)
87 options.emplace_back(str, OptionData{&cliOption, phase,
length});
91 std::initializer_list<string_view> extensions,
CLIFileType& cliFileType)
94 [&](
auto& ext) {
return std::pair(ext, &cliFileType); }));
97 bool CommandLineParser::parseOption(
98 const string& arg,
span<string>& cmdLine, ParsePhase phase)
101 (it !=
end(options)) && (it->first == arg)) {
103 if (it->second.phase <= phase) {
105 it->second.option->parseOption(arg, cmdLine);
107 }
catch (MSXException& e) {
108 throw FatalError(std::move(e).getMessage());
115 bool CommandLineParser::parseFileName(
const string& arg,
span<string>& cmdLine)
117 if (
auto* handler = getFileTypeHandlerForFileName(arg)) {
120 handler->parseFileType(arg, cmdLine);
122 }
catch (MSXException& e) {
123 throw FatalError(std::move(e).getMessage());
129 CLIFileType* CommandLineParser::getFileTypeHandlerForFileName(string_view
filename)
const
131 auto inner = [&](string_view arg) -> CLIFileType* {
133 if (extension.size() <= 1) {
136 extension.remove_prefix(1);
140 if ((it ==
end(fileTypes)) || !cmp(it->first, extension)) {
155 result = inner(file.getOriginalName());
156 }
catch (FileException&) {
171 vector<string> backupCmdLine;
179 fileTypeCategoryInfo = std::make_unique<FileTypeCategoryInfoTopic>(
189 cliComm.
addListener(std::make_unique<StdioMessages>());
192 auto& settingsConfig =
202 "Loading of settings failed: ",
204 "Reverting to default settings.");
208 throw FatalError(
"Error in default settings: ",
221 const auto& machine =
227 "Failed to initialize default machine: ",
230 const auto& fallbackMachine =
233 "Using fallback machine: ", fallbackMachine);
247 while (!cmdLine.
empty()) {
248 string arg = std::move(cmdLine.
front());
251 if (!parseOption(arg, cmdLine, phase)) {
254 !parseFileName(arg, cmdLine)) {
256 backupCmdLine.push_back(arg);
258 (it !=
end(options)) && (it->first == arg)) {
259 for (
unsigned i = 0; i < it->second.length - 1; ++i) {
260 if (cmdLine.
empty())
break;
261 backupCmdLine.push_back(std::move(cmdLine.
front()));
268 std::swap(backupCmdLine, cmdLineBuf);
269 backupCmdLine.clear();
270 cmdLine = cmdLineBuf;
275 opData.option->parseDone();
277 if (!cmdLine.
empty() && (parseStatus !=
EXIT)) {
279 "Error parsing command line: ", cmdLine.
front(),
"\n"
280 "Use \"openmsx -h\" to see a list of available options");
313 void CommandLineParser::ControlOption::parseOption(
316 const auto& fullType = getArgument(option, cmdLine);
320 auto& controller = parser.getGlobalCommandController();
321 auto& distributor = parser.reactor.getEventDistributor();
322 auto& cliComm = parser.reactor.getGlobalCliComm();
323 std::unique_ptr<CliListener> connection;
324 if (type ==
"stdio") {
325 connection = std::make_unique<StdioConnection>(
326 controller, distributor);
328 }
else if (type ==
"pipe") {
329 connection = std::make_unique<PipeConnection>(
330 controller, distributor, arguments);
333 throw FatalError(
"Unknown control type: '", type,
'\'');
335 cliComm.addListener(std::move(connection));
340 string_view CommandLineParser::ControlOption::optionHelp()
const
342 return "Enable external control of openMSX process";
348 void CommandLineParser::ScriptOption::parseOption(
351 parseFileType(getArgument(option, cmdLine), cmdLine);
354 string_view CommandLineParser::ScriptOption::optionHelp()
const
356 return "Run extra startup script";
359 void CommandLineParser::ScriptOption::parseFileType(
365 string_view CommandLineParser::ScriptOption::fileTypeHelp()
const
367 return "Extra Tcl script to run at startup";
370 string_view CommandLineParser::ScriptOption::fileTypeCategoryName()
const
377 void CommandLineParser::CommandOption::parseOption(
380 commands.push_back(getArgument(option, cmdLine));
383 std::string_view CommandLineParser::CommandOption::optionHelp()
const
385 return "Run Tcl command at startup (see also -script)";
391 static string formatSet(
const vector<string_view>& inputSet, string::size_type columns)
394 string::size_type totalLength = 0;
395 for (
const auto& temp : inputSet) {
396 if (totalLength == 0) {
399 totalLength = temp.size();
402 if ((totalLength + temp.size()) > columns) {
404 totalLength = temp.size();
407 totalLength += 2 + temp.size();
411 if (totalLength < columns) {
412 outString.append(columns - totalLength,
' ');
417 static string formatHelptext(string_view helpText,
421 string_view::size_type index = 0;
422 while (helpText.substr(index).size() >
maxLength) {
423 auto pos = helpText.substr(index,
maxLength).rfind(
' ');
424 if (pos == string_view::npos) {
425 pos = helpText.substr(
maxLength).find(
' ');
426 if (pos == string_view::npos) {
427 pos = helpText.substr(index).size();
430 strAppend(outText, helpText.substr(index, index + pos),
'\n',
434 strAppend(outText, helpText.substr(index));
443 return strCat(formatSet(p.second, 15),
' ',
444 formatHelptext(p.first, 50, 20));
447 for (
auto& s : printSet) {
455 void CommandLineParser::HelpOption::parseOption(
458 auto& parser =
OUTER(CommandLineParser, helpOption);
460 cout << fullVersion <<
'\n'
461 << string(fullVersion.size(),
'=') <<
"\n"
463 "usage: openmsx [arguments]\n"
464 " an argument is either an option or a filename\n"
466 " this is the list of supported options:\n";
469 for (
const auto& [name, data] : parser.options) {
470 const auto& helpText = data.option->optionHelp();
471 if (!helpText.empty()) {
472 itemMap[helpText].push_back(name);
475 printItemMap(itemMap);
478 " this is the list of supported file types:\n";
481 for (
const auto& [ext, data] : parser.fileTypes) {
482 itemMap[data->fileTypeHelp()].push_back(ext);
484 printItemMap(itemMap);
489 string_view CommandLineParser::HelpOption::optionHelp()
const
491 return "Shows this text";
497 void CommandLineParser::VersionOption::parseOption(
501 "flavour: " << BUILD_FLAVOUR <<
"\n"
502 "components: " << BUILD_COMPONENTS <<
'\n';
503 auto& parser =
OUTER(CommandLineParser, versionOption);
507 string_view CommandLineParser::VersionOption::optionHelp()
const
509 return "Prints openMSX version and exits";
515 void CommandLineParser::MachineOption::parseOption(
518 auto& parser =
OUTER(CommandLineParser, machineOption);
519 if (parser.haveConfig) {
520 throw FatalError(
"Only one machine option allowed");
523 parser.reactor.switchMachine(getArgument(option, cmdLine));
524 }
catch (MSXException& e) {
525 throw FatalError(std::move(e).getMessage());
527 parser.haveConfig =
true;
530 string_view CommandLineParser::MachineOption::optionHelp()
const
532 return "Use machine specified in argument";
538 void CommandLineParser::SettingOption::parseOption(
541 auto& parser =
OUTER(CommandLineParser, settingOption);
542 if (parser.haveSettings) {
543 throw FatalError(
"Only one setting option allowed");
546 auto& settingsConfig = parser.reactor.getGlobalCommandController().getSettingsConfig();
547 settingsConfig.loadSetting(
549 parser.haveSettings =
true;
550 }
catch (FileException& e) {
551 throw FatalError(std::move(e).getMessage());
552 }
catch (ConfigException& e) {
553 throw FatalError(std::move(e).getMessage());
557 string_view CommandLineParser::SettingOption::optionHelp()
const
559 return "Load an alternative settings file";
565 void CommandLineParser::TestConfigOption::parseOption(
568 auto& parser =
OUTER(CommandLineParser, testConfigOption);
572 string_view CommandLineParser::TestConfigOption::optionHelp()
const
574 return "Test if the specified config works and exit";
579 void CommandLineParser::BashOption::parseOption(
582 auto& parser =
OUTER(CommandLineParser, bashOption);
583 string_view last = cmdLine.
empty() ? string_view{} : cmdLine.
front();
584 cmdLine = cmdLine.
subspan(0, 0);
586 if (last ==
"-machine") {
594 }
else if (last ==
"-romtype") {
599 for (
const auto& name :
view::keys(parser.options)) {
600 cout << name <<
'\n';
606 string_view CommandLineParser::BashOption::optionHelp()
const
613 CommandLineParser::FileTypeCategoryInfoTopic::FileTypeCategoryInfoTopic(
614 InfoCommand& openMSXInfoCommand,
const CommandLineParser& parser_)
615 : InfoTopic(openMSXInfoCommand,
"file_type_category")
620 void CommandLineParser::FileTypeCategoryInfoTopic::execute(
623 checkNumArgs(tokens, 3,
"filename");
624 assert(tokens.
size() == 3);
627 std::string_view fileName = tokens[2].getString();
628 if (
const auto* handler = parser.getFileTypeHandlerForFileName(fileName)) {
629 result.addListElement(handler->fileTypeCategoryName());
631 result.addListElement(
"unknown");
635 string CommandLineParser::FileTypeCategoryInfoTopic::help(
const std::vector<std::string>& )
const
637 return "Returns the file type category for the given file.";
void printInfo(std::string_view message)
void printWarning(std::string_view message)
CommandLineParser(Reactor &reactor)
bool isHiddenStartup() const
Need to suppress renderer window on startup?
void registerOption(const char *str, CLIOption &cliOption, ParsePhase phase=PHASE_LAST, unsigned length=2)
void parse(int argc, char **argv)
void registerFileType(std::initializer_list< std::string_view > extensions, CLIFileType &cliFileType)
ParseStatus getParseStatus() const
GlobalCommandController & getGlobalCommandController() const
Interpreter & getInterpreter() const
MSXMotherBoard * getMotherBoard() const
void addListener(std::unique_ptr< CliListener > listener)
SettingsConfig & getSettingsConfig()
void init(const char *programName)
const std::string & getMessage() const &
Contains the main loop of openMSX.
MSXMotherBoard * getMotherBoard() const
InfoCommand & getOpenMSXInfoCommand()
void switchMachine(const std::string &machine)
GlobalCliComm & getGlobalCliComm()
EnumSetting< int > & getMachineSetting()
Interpreter & getInterpreter()
GlobalCommandController & getGlobalCommandController()
static std::vector< std::string > getHwConfigs(std::string_view type)
static std::vector< std::string_view > getAllRomTypes()
static std::string full()
constexpr subspan_return_t< Offset, Count > subspan() const
constexpr reference front() const
constexpr index_type size() const noexcept
constexpr bool empty() const noexcept
#define COMPONENT_LASERDISC
std::pair< string_view, string_view > splitOnFirst(string_view str, string_view chars)
bool startsWith(string_view total, string_view part)
T length(const vecN< N, T > &x)
std::unique_ptr< Context > context
const std::string & getConventionalPath(const std::string &path)
Returns the path in conventional path-delimiter.
string_view getExtension(string_view path)
Returns the extension portion of a path.
This file implemented 3 utility functions:
const FileContext & systemFileContext()
LessTupleElement< 0 > CmpOptions
hash_map< string_view, vector< string_view >, XXHasher > GroupedItems
constexpr unsigned maxLength
const FileContext & currentDirFileContext()
CmpTupleElement< 0, StringOp::caseless > CmpFileTypes
constexpr const char *const filename
FileContext userFileContext(string_view savePath)
auto lower_bound(ForwardRange &&range, const T &value)
void sort(RandomAccessRange &&range)
constexpr auto transform(Range &&range, UnaryOp op)
constexpr auto keys(Map &&map)
constexpr auto values(Map &&map)
#define OUTER(type, member)
auto to_vector(Range &&range) -> std::vector< detail::ToVectorType< T, decltype(std::begin(range))>>
strCatImpl::ConcatSpaces spaces(size_t n)
std::string strCat(Ts &&...ts)
void strAppend(std::string &result, Ts &&...ts)
constexpr auto xrange(T e)
constexpr auto end(const zstring_view &x)