#include "globals.h" #include "flags.h" static FlagGroup* currentFlagGroup; static std::vector all_flags; static std::map flags_by_name; static void doHelp(); static FlagGroup helpGroup; static ActionFlag helpFlag = ActionFlag( { "--help", "-h" }, "Shows the help.", doHelp); FlagGroup::FlagGroup(const std::initializer_list groups): _groups(groups.begin(), groups.end()) { currentFlagGroup = this; } FlagGroup::FlagGroup() { currentFlagGroup = this; } void FlagGroup::addFlag(Flag* flag) { _flags.push_back(flag); } std::vector FlagGroup::parseFlagsWithFilenames(int argc, const char* argv[]) { if (_initialised) throw std::runtime_error("called parseFlags() twice"); /* Recursively accumulate a list of all flags. */ all_flags.clear(); flags_by_name.clear(); std::function recurse; recurse = [&](FlagGroup* group) { if (group->_initialised) return; for (FlagGroup* subgroup : group->_groups) recurse(subgroup); for (Flag* flag : group->_flags) { for (const auto& name : flag->names()) { if (flags_by_name.find(name) != flags_by_name.end()) Error() << "two flags use the name '" << name << "'"; flags_by_name[name] = flag; } all_flags.push_back(flag); } group->_initialised = true; }; recurse(this); recurse(&helpGroup); /* Now actually parse them. */ std::vector filenames; int index = 1; while (index < argc) { std::string thisarg = argv[index]; std::string thatarg = (index < (argc-1)) ? argv[index+1] : ""; std::string key; std::string value; bool usesthat = false; if (thisarg.size() == 0) { /* Ignore this argument. */ } else if (thisarg[0] != '-') { /* This is a filename. */ filenames.push_back(thisarg); } else { /* This is a flag. */ if ((thisarg.size() > 1) && (thisarg[1] == '-')) { /* Long option. */ auto equals = thisarg.rfind('='); if (equals != std::string::npos) { key = thisarg.substr(0, equals); value = thisarg.substr(equals+1); } else { key = thisarg; value = thatarg; usesthat = true; } } else { /* Short option. */ if (thisarg.size() > 2) { key = thisarg.substr(0, 2); value = thisarg.substr(2); } else { key = thisarg; value = thatarg; usesthat = true; } } auto flag = flags_by_name.find(key); if (flag == flags_by_name.end()) Error() << "unknown flag '" << key << "'; try --help"; flag->second->set(value); if (usesthat && flag->second->hasArgument()) index++; } index++; } return filenames; } void FlagGroup::parseFlags(int argc, const char* argv[]) { auto filenames = parseFlagsWithFilenames(argc, argv); if (!filenames.empty()) Error() << "non-option parameter " << *filenames.begin() << " seen (try --help)"; } void FlagGroup::checkInitialised() const { if (!_initialised) throw std::runtime_error("Attempt to access uninitialised flag"); } Flag::Flag(const std::vector& names, const std::string helptext): _group(*currentFlagGroup), _names(names), _helptext(helptext) { if (!currentFlagGroup) Error() << "no flag group defined for " << *names.begin(); _group.addFlag(this); } void BoolFlag::set(const std::string& value) { if ((value == "true") || (value == "y")) _value = true; else if ((value == "false") || (value == "n")) _value = false; else Error() << "can't parse '" << value << "'; try 'true' or 'false'"; } static void doHelp() { std::cout << "FluxEngine options:" << std::endl; for (auto flag : all_flags) { std::cout << " "; bool firstname = true; for (auto name : flag->names()) { if (!firstname) std::cout << ", "; std::cout << name; firstname = false; } if (flag->hasArgument()) std::cout << " defaultValueAsString() << "\">"; std::cout << ": " << flag->helptext() << std::endl; } exit(0); }