34 using std::string_view;
46 , cassettePlayerCLI(*this)
48 , laserdiscPlayerCLI(*this)
53 , parseStatus(UNPARSED)
81 options.emplace_back(OptionData{str, &cliOption, phase,
length});
85 std::initializer_list<string_view> extensions,
CLIFileType& cliFileType)
88 [&](
auto& ext) {
return FileTypeData{ext, &cliFileType}; }));
91 bool CommandLineParser::parseOption(
92 const string& arg,
span<string>& cmdLine, ParsePhase phase)
95 (it !=
end(options)) && (it->name == arg)) {
97 if (it->phase <= phase) {
99 it->option->parseOption(arg, cmdLine);
101 }
catch (MSXException& e) {
102 throw FatalError(std::move(e).getMessage());
109 bool CommandLineParser::parseFileName(
const string& arg,
span<string>& cmdLine)
111 if (
auto* handler = getFileTypeHandlerForFileName(arg)) {
114 handler->parseFileType(arg, cmdLine);
116 }
catch (MSXException& e) {
117 throw FatalError(std::move(e).getMessage());
123 CLIFileType* CommandLineParser::getFileTypeHandlerForFileName(string_view
filename)
const
125 auto inner = [&](string_view arg) -> CLIFileType* {
127 if (extension.size() <= 1) {
130 extension.remove_prefix(1);
133 &FileTypeData::extension);
135 if ((it ==
end(fileTypes)) || !cmp(it->extension, extension)) {
150 result = inner(file.getOriginalName());
151 }
catch (FileException&) {
166 std::vector<string> backupCmdLine;
174 fileTypeCategoryInfo.emplace(
184 cliComm.
addListener(std::make_unique<StdioMessages>());
187 auto& settingsConfig =
197 "Loading of settings failed: ",
199 "Reverting to default settings.");
203 throw FatalError(
"Error in default settings: ",
216 const auto& machine =
222 "Failed to initialize default machine: ",
225 const auto& fallbackMachine =
228 "Using fallback machine: ", fallbackMachine);
242 while (!cmdLine.
empty()) {
243 string arg = std::move(cmdLine.
front());
246 if (!parseOption(arg, cmdLine, phase)) {
249 !parseFileName(arg, cmdLine)) {
251 backupCmdLine.push_back(arg);
253 (it !=
end(options)) && (it->name == arg)) {
254 for (
unsigned i = 0; i < it->length - 1; ++i) {
255 if (cmdLine.
empty())
break;
256 backupCmdLine.push_back(std::move(cmdLine.
front()));
263 std::swap(backupCmdLine, cmdLineBuf);
264 backupCmdLine.clear();
265 cmdLine = cmdLineBuf;
269 for (
const auto& option : options) {
270 option.option->parseDone();
272 if (!cmdLine.
empty() && (parseStatus !=
EXIT)) {
274 "Error parsing command line: ", cmdLine.
front(),
"\n"
275 "Use \"openmsx -h\" to see a list of available options");
308 void CommandLineParser::ControlOption::parseOption(
311 const auto& fullType = getArgument(option, cmdLine);
315 auto& controller = parser.getGlobalCommandController();
316 auto& distributor = parser.reactor.getEventDistributor();
317 auto& cliComm = parser.reactor.getGlobalCliComm();
318 std::unique_ptr<CliListener> connection;
319 if (type ==
"stdio") {
320 connection = std::make_unique<StdioConnection>(
321 controller, distributor);
323 }
else if (type ==
"pipe") {
324 connection = std::make_unique<PipeConnection>(
325 controller, distributor, arguments);
328 throw FatalError(
"Unknown control type: '", type,
'\'');
330 cliComm.addListener(std::move(connection));
335 string_view CommandLineParser::ControlOption::optionHelp()
const
337 return "Enable external control of openMSX process";
343 void CommandLineParser::ScriptOption::parseOption(
346 parseFileType(getArgument(option, cmdLine), cmdLine);
349 string_view CommandLineParser::ScriptOption::optionHelp()
const
351 return "Run extra startup script";
354 void CommandLineParser::ScriptOption::parseFileType(
360 string_view CommandLineParser::ScriptOption::fileTypeHelp()
const
362 return "Extra Tcl script to run at startup";
365 string_view CommandLineParser::ScriptOption::fileTypeCategoryName()
const
372 void CommandLineParser::CommandOption::parseOption(
375 commands.push_back(getArgument(option, cmdLine));
378 std::string_view CommandLineParser::CommandOption::optionHelp()
const
380 return "Run Tcl command at startup (see also -script)";
389 string::size_type totalLength = 0;
390 for (
const auto& temp : inputSet) {
391 if (totalLength == 0) {
394 totalLength = temp.size();
397 if ((totalLength + temp.size()) > columns) {
399 totalLength = temp.size();
402 totalLength += 2 + temp.size();
406 if (totalLength < columns) {
407 outString.append(columns - totalLength,
' ');
412 static string formatHelptext(string_view helpText,
416 string_view::size_type index = 0;
417 while (helpText.substr(index).size() >
maxLength) {
418 auto pos = helpText.substr(index,
maxLength).rfind(
' ');
419 if (pos == string_view::npos) {
420 pos = helpText.substr(
maxLength).find(
' ');
421 if (pos == string_view::npos) {
422 pos = helpText.substr(index).size();
425 strAppend(outText, helpText.substr(index, index + pos),
'\n',
429 strAppend(outText, helpText.substr(index));
438 return strCat(formatSet(p.second, 15),
' ',
439 formatHelptext(p.first, 50, 20));
442 for (
auto& s : printSet) {
450 void CommandLineParser::HelpOption::parseOption(
453 auto& parser =
OUTER(CommandLineParser, helpOption);
455 cout << fullVersion <<
'\n'
456 << string(fullVersion.size(),
'=') <<
"\n"
458 "usage: openmsx [arguments]\n"
459 " an argument is either an option or a filename\n"
461 " this is the list of supported options:\n";
464 for (
const auto& option : parser.options) {
465 const auto& helpText = option.option->optionHelp();
466 if (!helpText.empty()) {
467 itemMap[helpText].push_back(option.name);
470 printItemMap(itemMap);
473 " this is the list of supported file types:\n";
476 for (
const auto& [extension, fileType] : parser.fileTypes) {
477 itemMap[fileType->fileTypeHelp()].push_back(extension);
479 printItemMap(itemMap);
484 string_view CommandLineParser::HelpOption::optionHelp()
const
486 return "Shows this text";
492 void CommandLineParser::VersionOption::parseOption(
496 "flavour: " << BUILD_FLAVOUR <<
"\n"
497 "components: " << BUILD_COMPONENTS <<
'\n';
498 auto& parser =
OUTER(CommandLineParser, versionOption);
502 string_view CommandLineParser::VersionOption::optionHelp()
const
504 return "Prints openMSX version and exits";
510 void CommandLineParser::MachineOption::parseOption(
513 auto& parser =
OUTER(CommandLineParser, machineOption);
514 if (parser.haveConfig) {
515 throw FatalError(
"Only one machine option allowed");
518 parser.reactor.switchMachine(getArgument(option, cmdLine));
519 }
catch (MSXException& e) {
520 throw FatalError(std::move(e).getMessage());
522 parser.haveConfig =
true;
525 string_view CommandLineParser::MachineOption::optionHelp()
const
527 return "Use machine specified in argument";
533 void CommandLineParser::SettingOption::parseOption(
536 auto& parser =
OUTER(CommandLineParser, settingOption);
537 if (parser.haveSettings) {
538 throw FatalError(
"Only one setting option allowed");
541 auto& settingsConfig = parser.reactor.getGlobalCommandController().getSettingsConfig();
542 settingsConfig.loadSetting(
544 parser.haveSettings =
true;
545 }
catch (FileException& e) {
546 throw FatalError(std::move(e).getMessage());
547 }
catch (ConfigException& e) {
548 throw FatalError(std::move(e).getMessage());
552 string_view CommandLineParser::SettingOption::optionHelp()
const
554 return "Load an alternative settings file";
560 void CommandLineParser::TestConfigOption::parseOption(
563 auto& parser =
OUTER(CommandLineParser, testConfigOption);
567 string_view CommandLineParser::TestConfigOption::optionHelp()
const
569 return "Test if the specified config works and exit";
574 void CommandLineParser::BashOption::parseOption(
577 auto& parser =
OUTER(CommandLineParser, bashOption);
578 string_view last = cmdLine.
empty() ? string_view{} : cmdLine.
front();
579 cmdLine = cmdLine.
subspan(0, 0);
581 if (last ==
"-machine") {
585 }
else if (last.starts_with(
"-ext")) {
589 }
else if (last ==
"-romtype") {
594 for (
const auto& option : parser.options) {
595 cout << option.name <<
'\n';
601 string_view CommandLineParser::BashOption::optionHelp()
const
608 CommandLineParser::FileTypeCategoryInfoTopic::FileTypeCategoryInfoTopic(
609 InfoCommand& openMSXInfoCommand,
const CommandLineParser& parser_)
610 : InfoTopic(openMSXInfoCommand,
"file_type_category")
615 void CommandLineParser::FileTypeCategoryInfoTopic::execute(
618 checkNumArgs(tokens, 3,
"filename");
619 assert(tokens.
size() == 3);
622 std::string_view fileName = tokens[2].getString();
623 if (
const auto* handler = parser.getFileTypeHandlerForFileName(fileName)) {
624 result.addListElement(handler->fileTypeCategoryName());
626 result.addListElement(
"unknown");
632 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 auto 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)
T length(const vecN< N, T > &x)
std::optional< 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()
hash_map< string_view, std::vector< string_view >, XXHasher > GroupedItems
constexpr unsigned maxLength
const FileContext & currentDirFileContext()
constexpr const char *const filename
FileContext userFileContext(string_view savePath)
void sort(RandomAccessRange &&range)
auto lower_bound(ForwardRange &&range, const T &value, Compare comp={}, Proj proj={})
constexpr auto transform(Range &&range, UnaryOp op)
#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)