34using std::string_view;
46 , cassettePlayerCLI(*this)
48 , laserdiscPlayerCLI(*this)
78 options.emplace_back(str, &cliOption, phase, length);
82 std::span<const string_view> extensions,
CLIFileType& cliFileType)
85 [&](
auto& ext) {
return FileTypeData{ext, &cliFileType}; }));
88bool CommandLineParser::parseOption(
89 const string& arg, std::span<string>& cmdLine, ParsePhase phase)
91 if (
auto o =
binary_find(options, arg, {}, &OptionData::name)) {
93 if (o->phase <= phase) {
95 o->option->parseOption(arg, cmdLine);
97 }
catch (MSXException& e) {
98 throw FatalError(std::move(e).getMessage());
105bool CommandLineParser::parseFileName(
const string& arg, std::span<string>& cmdLine)
107 if (
auto* handler = getFileTypeHandlerForFileName(arg)) {
110 handler->parseFileType(arg, cmdLine);
112 }
catch (MSXException& e) {
113 throw FatalError(std::move(e).getMessage());
119CLIFileType* CommandLineParser::getFileTypeHandlerForFileName(string_view filename)
const
121 auto inner = [&](string_view arg) -> CLIFileType* {
123 if (extension.size() <= 1) {
126 extension.remove_prefix(1);
129 &FileTypeData::extension);
130 return f ? f->fileType :
nullptr;
138 auto* result = inner(filename);
142 result = inner(file.getOriginalName());
143 }
catch (FileException&) {
157 std::span<string> cmdLine(cmdLineBuf);
158 std::vector<string> backupCmdLine;
166 fileTypeCategoryInfo.emplace(
176 cliComm.
addListener(std::make_unique<StdioMessages>());
179 auto& settingsConfig =
184 string filename =
"settings.xml";
186 settingsConfig.loadSetting(context, filename);
189 "Loading of settings failed: ",
191 "Reverting to default settings.");
195 throw FatalError(
"Error in default settings: ",
202 settingsConfig.setSaveFilename(context, filename);
208 const auto& machine =
214 "Failed to initialize default machine: ",
217 auto fallbackMachine = std::string(
220 "Using fallback machine: ", fallbackMachine);
234 while (!cmdLine.empty()) {
235 string arg = std::move(cmdLine.front());
236 cmdLine = cmdLine.subspan(1);
238 if (!parseOption(arg, cmdLine, phase)) {
241 !parseFileName(arg, cmdLine)) {
243 backupCmdLine.push_back(arg);
244 if (
auto o =
binary_find(options, arg, {}, &OptionData::name)) {
245 for (
unsigned i = 0; i < o->length - 1; ++i) {
246 if (cmdLine.empty())
break;
247 backupCmdLine.push_back(std::move(cmdLine.front()));
248 cmdLine = cmdLine.subspan(1);
254 std::swap(backupCmdLine, cmdLineBuf);
255 backupCmdLine.clear();
256 cmdLine = cmdLineBuf;
260 for (
const auto& option : options) {
261 option.option->parseDone();
263 if (!cmdLine.empty() && (parseStatus !=
EXIT)) {
265 "Error parsing command line: ", cmdLine.front(),
"\n"
266 "Use \"openmsx -h\" to see a list of available options");
294void CommandLineParser::ControlOption::parseOption(
295 const string& option, std::span<string>& cmdLine)
297 const auto& fullType = getArgument(option, cmdLine);
301 auto& controller = parser.getGlobalCommandController();
302 auto& distributor = parser.reactor.getEventDistributor();
303 auto& cliComm = parser.reactor.getGlobalCliComm();
304 std::unique_ptr<CliListener> connection;
305 if (type ==
"stdio") {
306 connection = std::make_unique<StdioConnection>(
307 controller, distributor);
309 }
else if (type ==
"pipe") {
310 connection = std::make_unique<PipeConnection>(
311 controller, distributor, arguments);
314 throw FatalError(
"Unknown control type: '", type,
'\'');
316 cliComm.addListener(std::move(connection));
321string_view CommandLineParser::ControlOption::optionHelp()
const
323 return "Enable external control of openMSX process";
329void CommandLineParser::ScriptOption::parseOption(
330 const string& option, std::span<string>& cmdLine)
332 parseFileType(getArgument(option, cmdLine), cmdLine);
335string_view CommandLineParser::ScriptOption::optionHelp()
const
337 return "Run extra startup script";
340void CommandLineParser::ScriptOption::parseFileType(
341 const string& filename, std::span<std::string>& )
343 scripts.push_back(filename);
346string_view CommandLineParser::ScriptOption::fileTypeHelp()
const
348 return "Extra Tcl script to run at startup";
351string_view CommandLineParser::ScriptOption::fileTypeCategoryName()
const
358void CommandLineParser::CommandOption::parseOption(
359 const std::string& option, std::span<std::string>& cmdLine)
361 commands.push_back(getArgument(option, cmdLine));
364std::string_view CommandLineParser::CommandOption::optionHelp()
const
366 return "Run Tcl command at startup (see also -script)";
372static string formatSet(std::span<const string_view> inputSet, string::size_type columns)
375 string::size_type totalLength = 0;
376 for (
const auto& temp : inputSet) {
377 if (totalLength == 0) {
380 totalLength = temp.size();
383 if ((totalLength + temp.size()) > columns) {
385 totalLength = temp.size();
388 totalLength += 2 + temp.size();
392 if (totalLength < columns) {
393 outString.append(columns - totalLength,
' ');
398static string formatHelpText(string_view helpText,
399 unsigned maxLength,
unsigned indent)
402 string_view::size_type index = 0;
403 while (helpText.substr(index).size() > maxLength) {
404 auto pos = helpText.substr(index, maxLength).rfind(
' ');
405 if (pos == string_view::npos) {
406 pos = helpText.substr(maxLength).find(
' ');
407 if (pos == string_view::npos) {
408 pos = helpText.substr(index).size();
411 strAppend(outText, helpText.substr(index, index + pos),
'\n',
415 strAppend(outText, helpText.substr(index));
424 return strCat(formatSet(p.second, 15),
' ',
425 formatHelpText(p.first, 50, 20));
428 for (
const auto& s : printSet) {
436void CommandLineParser::HelpOption::parseOption(
437 const string& , std::span<string>& )
439 auto& parser =
OUTER(CommandLineParser, helpOption);
441 cout << fullVersion <<
'\n'
442 << string(fullVersion.size(),
'=') <<
"\n"
444 "usage: openmsx [arguments]\n"
445 " an argument is either an option or a filename\n"
447 " this is the list of supported options:\n";
450 for (
const auto& option : parser.options) {
451 const auto& helpText = option.option->optionHelp();
452 if (!helpText.empty()) {
453 itemMap[helpText].push_back(option.name);
456 printItemMap(itemMap);
459 " this is the list of supported file types:\n";
462 for (
const auto& [extension, fileType] : parser.fileTypes) {
463 itemMap[fileType->fileTypeHelp()].push_back(extension);
465 printItemMap(itemMap);
470string_view CommandLineParser::HelpOption::optionHelp()
const
472 return "Shows this text";
478void CommandLineParser::VersionOption::parseOption(
479 const string& , std::span<string>& )
482 "flavour: " << BUILD_FLAVOUR <<
"\n"
483 "components: " << BUILD_COMPONENTS <<
'\n';
484 auto& parser =
OUTER(CommandLineParser, versionOption);
488string_view CommandLineParser::VersionOption::optionHelp()
const
490 return "Prints openMSX version and exits";
496void CommandLineParser::MachineOption::parseOption(
497 const string& option, std::span<string>& cmdLine)
499 auto& parser =
OUTER(CommandLineParser, machineOption);
500 if (parser.haveConfig) {
501 throw FatalError(
"Only one machine option allowed");
504 parser.reactor.switchMachine(getArgument(option, cmdLine));
505 }
catch (MSXException& e) {
506 throw FatalError(std::move(e).getMessage());
508 parser.haveConfig =
true;
511string_view CommandLineParser::MachineOption::optionHelp()
const
513 return "Use machine specified in argument";
519void CommandLineParser::SettingOption::parseOption(
520 const string& option, std::span<string>& cmdLine)
522 auto& parser =
OUTER(CommandLineParser, settingOption);
523 if (parser.haveSettings) {
524 throw FatalError(
"Only one setting option allowed");
527 auto& settingsConfig = parser.reactor.getGlobalCommandController().getSettingsConfig();
528 settingsConfig.loadSetting(
530 parser.haveSettings =
true;
531 }
catch (FileException& e) {
532 throw FatalError(std::move(e).getMessage());
533 }
catch (ConfigException& e) {
534 throw FatalError(std::move(e).getMessage());
538string_view CommandLineParser::SettingOption::optionHelp()
const
540 return "Load an alternative settings file";
546void CommandLineParser::TestConfigOption::parseOption(
547 const string& , std::span<string>& )
549 auto& parser =
OUTER(CommandLineParser, testConfigOption);
553string_view CommandLineParser::TestConfigOption::optionHelp()
const
555 return "Test if the specified config works and exit";
560void CommandLineParser::BashOption::parseOption(
561 const string& , std::span<string>& cmdLine)
563 auto& parser =
OUTER(CommandLineParser, bashOption);
564 string_view last = cmdLine.empty() ? string_view{} : cmdLine.front();
565 cmdLine = cmdLine.subspan(0, 0);
567 if (last ==
"-machine") {
568 for (
const auto& s : Reactor::getHwConfigs(
"machines")) {
571 }
else if (last.starts_with(
"-ext")) {
572 for (
const auto& s : Reactor::getHwConfigs(
"extensions")) {
575 }
else if (last ==
"-romtype") {
576 for (
const auto& s : RomInfo::getAllRomTypes()) {
580 for (
const auto& option : parser.options) {
581 cout << option.name <<
'\n';
587string_view CommandLineParser::BashOption::optionHelp()
const
594CommandLineParser::FileTypeCategoryInfoTopic::FileTypeCategoryInfoTopic(
595 InfoCommand& openMSXInfoCommand,
const CommandLineParser& parser_)
596 : InfoTopic(openMSXInfoCommand,
"file_type_category")
601void CommandLineParser::FileTypeCategoryInfoTopic::execute(
602 std::span<const TclObject> tokens, TclObject& result)
const
604 checkNumArgs(tokens, 3,
"filename");
605 assert(tokens.size() == 3);
608 std::string_view fileName = tokens[2].getString();
609 if (
const auto* handler = parser.getFileTypeHandlerForFileName(fileName)) {
610 result.addListElement(handler->fileTypeCategoryName());
612 result.addListElement(
"unknown");
616string CommandLineParser::FileTypeCategoryInfoTopic::help(std::span<const TclObject> )
const
618 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)
void registerOption(const char *str, CLIOption &cliOption, ParsePhase phase=PHASE_LAST, unsigned length=2)
ParseStatus getParseStatus() const
void registerFileType(std::span< const std::string_view > extensions, CLIFileType &cliFileType)
void parse(std::span< char * > argv)
GlobalCommandController & getGlobalCommandController() const
Interpreter & getInterpreter() const
MSXMotherBoard * getMotherBoard() const
std::string_view getString() const
CliListener * addListener(std::unique_ptr< CliListener > listener)
SettingsConfig & getSettingsConfig()
void init(const char *programName) const
Contains the main loop of openMSX.
MSXMotherBoard * getMotherBoard() const
GlobalCommandController & getGlobalCommandController()
InfoCommand & getOpenMSXInfoCommand()
void switchMachine(const std::string &machine)
EnumSetting< int > & getMachineSetting()
GlobalCliComm & getGlobalCliComm()
Interpreter & getInterpreter()
TclObject getDefaultValue() const final
Get the default value of this setting.
zstring_view getString() const
static std::string full()
#define COMPONENT_LASERDISC
std::pair< string_view, string_view > splitOnFirst(string_view str, string_view chars)
string_view getExtension(string_view path)
Returns the extension portion of a path.
const std::string & getConventionalPath(const std::string &path)
Returns the path in conventional path-delimiter.
This file implemented 3 utility functions:
const FileContext & systemFileContext()
hash_map< string_view, std::vector< string_view >, XXHasher > GroupedItems
const FileContext & currentDirFileContext()
const FileContext & userFileContext()
constexpr void sort(RandomAccessRange &&range)
constexpr auto transform(Range &&range, UnaryOp op)
constexpr auto drop(Range &&range, size_t n)
#define OUTER(type, member)
auto * binary_find(ForwardRange &&range, const T &value, Compare comp={}, Proj proj={})
auto to_vector(Range &&range) -> std::vector< detail::ToVectorType< T, decltype(std::begin(range))> >
void strAppend(std::string &result, Ts &&...ts)