34using std::string_view;
46 , cassettePlayerCLI(*this)
48 , laserdiscPlayerCLI(*this)
78 options.emplace_back(OptionData{str, &cliOption, phase,
length});
82 std::initializer_list<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 const auto& fallbackMachine =
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);
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");
299void CommandLineParser::ControlOption::parseOption(
300 const string& option, std::span<string>& cmdLine)
302 const auto& fullType = getArgument(option, cmdLine);
306 auto& controller = parser.getGlobalCommandController();
307 auto& distributor = parser.reactor.getEventDistributor();
308 auto& cliComm = parser.reactor.getGlobalCliComm();
309 std::unique_ptr<CliListener> connection;
310 if (type ==
"stdio") {
311 connection = std::make_unique<StdioConnection>(
312 controller, distributor);
314 }
else if (type ==
"pipe") {
315 connection = std::make_unique<PipeConnection>(
316 controller, distributor, arguments);
319 throw FatalError(
"Unknown control type: '", type,
'\'');
321 cliComm.addListener(std::move(connection));
326string_view CommandLineParser::ControlOption::optionHelp()
const
328 return "Enable external control of openMSX process";
334void CommandLineParser::ScriptOption::parseOption(
335 const string& option, std::span<string>& cmdLine)
337 parseFileType(getArgument(option, cmdLine), cmdLine);
340string_view CommandLineParser::ScriptOption::optionHelp()
const
342 return "Run extra startup script";
345void CommandLineParser::ScriptOption::parseFileType(
346 const string& filename, std::span<std::string>& )
348 scripts.push_back(filename);
351string_view CommandLineParser::ScriptOption::fileTypeHelp()
const
353 return "Extra Tcl script to run at startup";
356string_view CommandLineParser::ScriptOption::fileTypeCategoryName()
const
363void CommandLineParser::CommandOption::parseOption(
364 const std::string& option, std::span<std::string>& cmdLine)
366 commands.push_back(getArgument(option, cmdLine));
369std::string_view CommandLineParser::CommandOption::optionHelp()
const
371 return "Run Tcl command at startup (see also -script)";
377static string formatSet(std::span<const string_view> inputSet, string::size_type columns)
380 string::size_type totalLength = 0;
381 for (
const auto& temp : inputSet) {
382 if (totalLength == 0) {
385 totalLength = temp.size();
388 if ((totalLength + temp.size()) > columns) {
390 totalLength = temp.size();
393 totalLength += 2 + temp.size();
397 if (totalLength < columns) {
398 outString.append(columns - totalLength,
' ');
403static string formatHelpText(string_view helpText,
404 unsigned maxLength,
unsigned indent)
407 string_view::size_type index = 0;
408 while (helpText.substr(index).size() > maxLength) {
409 auto pos = helpText.substr(index, maxLength).rfind(
' ');
410 if (pos == string_view::npos) {
411 pos = helpText.substr(maxLength).find(
' ');
412 if (pos == string_view::npos) {
413 pos = helpText.substr(index).size();
416 strAppend(outText, helpText.substr(index, index + pos),
'\n',
420 strAppend(outText, helpText.substr(index));
429 return strCat(formatSet(p.second, 15),
' ',
430 formatHelpText(p.first, 50, 20));
433 for (
auto& s : printSet) {
441void CommandLineParser::HelpOption::parseOption(
442 const string& , std::span<string>& )
444 auto& parser =
OUTER(CommandLineParser, helpOption);
446 cout << fullVersion <<
'\n'
447 << string(fullVersion.size(),
'=') <<
"\n"
449 "usage: openmsx [arguments]\n"
450 " an argument is either an option or a filename\n"
452 " this is the list of supported options:\n";
455 for (
const auto& option : parser.options) {
456 const auto& helpText = option.option->optionHelp();
457 if (!helpText.empty()) {
458 itemMap[helpText].push_back(option.name);
461 printItemMap(itemMap);
464 " this is the list of supported file types:\n";
467 for (
const auto& [extension, fileType] : parser.fileTypes) {
468 itemMap[fileType->fileTypeHelp()].push_back(extension);
470 printItemMap(itemMap);
475string_view CommandLineParser::HelpOption::optionHelp()
const
477 return "Shows this text";
483void CommandLineParser::VersionOption::parseOption(
484 const string& , std::span<string>& )
487 "flavour: " << BUILD_FLAVOUR <<
"\n"
488 "components: " << BUILD_COMPONENTS <<
'\n';
489 auto& parser =
OUTER(CommandLineParser, versionOption);
493string_view CommandLineParser::VersionOption::optionHelp()
const
495 return "Prints openMSX version and exits";
501void CommandLineParser::MachineOption::parseOption(
502 const string& option, std::span<string>& cmdLine)
504 auto& parser =
OUTER(CommandLineParser, machineOption);
505 if (parser.haveConfig) {
506 throw FatalError(
"Only one machine option allowed");
509 parser.reactor.switchMachine(getArgument(option, cmdLine));
510 }
catch (MSXException&
e) {
511 throw FatalError(std::move(
e).getMessage());
513 parser.haveConfig =
true;
516string_view CommandLineParser::MachineOption::optionHelp()
const
518 return "Use machine specified in argument";
524void CommandLineParser::SettingOption::parseOption(
525 const string& option, std::span<string>& cmdLine)
527 auto& parser =
OUTER(CommandLineParser, settingOption);
528 if (parser.haveSettings) {
529 throw FatalError(
"Only one setting option allowed");
532 auto& settingsConfig = parser.reactor.getGlobalCommandController().getSettingsConfig();
533 settingsConfig.loadSetting(
535 parser.haveSettings =
true;
536 }
catch (FileException&
e) {
537 throw FatalError(std::move(
e).getMessage());
538 }
catch (ConfigException&
e) {
539 throw FatalError(std::move(
e).getMessage());
543string_view CommandLineParser::SettingOption::optionHelp()
const
545 return "Load an alternative settings file";
551void CommandLineParser::TestConfigOption::parseOption(
552 const string& , std::span<string>& )
554 auto& parser =
OUTER(CommandLineParser, testConfigOption);
558string_view CommandLineParser::TestConfigOption::optionHelp()
const
560 return "Test if the specified config works and exit";
565void CommandLineParser::BashOption::parseOption(
566 const string& , std::span<string>& cmdLine)
568 auto& parser =
OUTER(CommandLineParser, bashOption);
569 string_view last = cmdLine.empty() ? string_view{} : cmdLine.front();
570 cmdLine = cmdLine.subspan(0, 0);
572 if (last ==
"-machine") {
576 }
else if (last.starts_with(
"-ext")) {
580 }
else if (last ==
"-romtype") {
585 for (
const auto& option : parser.options) {
586 cout << option.name <<
'\n';
592string_view CommandLineParser::BashOption::optionHelp()
const
599CommandLineParser::FileTypeCategoryInfoTopic::FileTypeCategoryInfoTopic(
600 InfoCommand& openMSXInfoCommand,
const CommandLineParser& parser_)
601 : InfoTopic(openMSXInfoCommand,
"file_type_category")
606void CommandLineParser::FileTypeCategoryInfoTopic::execute(
607 std::span<const TclObject> tokens, TclObject& result)
const
609 checkNumArgs(tokens, 3,
"filename");
610 assert(tokens.size() == 3);
613 std::string_view fileName = tokens[2].getString();
614 if (
const auto* handler = parser.getFileTypeHandlerForFileName(fileName)) {
615 result.addListElement(handler->fileTypeCategoryName());
617 result.addListElement(
"unknown");
621string CommandLineParser::FileTypeCategoryInfoTopic::help(std::span<const TclObject> )
const
623 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 registerFileType(std::initializer_list< std::string_view > extensions, CLIFileType &cliFileType)
ParseStatus getParseStatus() const
void parse(std::span< char * > argv)
GlobalCommandController & getGlobalCommandController() const
Interpreter & getInterpreter() const
MSXMotherBoard * getMotherBoard() const
std::string_view getString() const
void addListener(std::unique_ptr< CliListener > listener)
SettingsConfig & getSettingsConfig()
void init(const char *programName)
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()
static std::vector< std::string > getHwConfigs(std::string_view type)
static auto getAllRomTypes()
TclObject getRestoreValue() const final
Get the value that will be set after a Tcl 'unset' command.
zstring_view getString() const
static std::string full()
#define COMPONENT_LASERDISC
std::pair< string_view, string_view > splitOnFirst(string_view str, string_view chars)
T length(const vecN< N, T > &x)
std::optional< Context > context
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()
FileContext userFileContext(string_view savePath)
constexpr void sort(RandomAccessRange &&range)
void swap(openmsx::MemBuffer< T > &l, openmsx::MemBuffer< T > &r) noexcept
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))> >
strCatImpl::ConcatSpaces spaces(size_t n)
std::string strCat(Ts &&...ts)
void strAppend(std::string &result, Ts &&...ts)