Mercurial > code
view C++/OptionParser.cpp @ 304:bae4af872cde
MFS
author | David Demelier <markand@malikania.fr> |
---|---|
date | Sat, 15 Nov 2014 13:19:30 +0100 |
parents | ff2db0ed78f1 |
children | 99e83685d4da |
line wrap: on
line source
/* * OptionParser.cpp -- safe getopt(3) replacement * * Copyright (c) 2013, 2014 David Demelier <markand@malikania.fr> * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include <iostream> #include <iomanip> #include "OptionParser.h" /* -------------------------------------------------------- * Option class * -------------------------------------------------------- */ Option::Option(const std::string &name, const std::string &help, Type type, int flags) : m_name(name) , m_help(help) , m_flags(flags) , m_type(type) { } const std::string &Option::name() const { return m_name; } const std::string &Option::token() const { return m_token; } const std::string &Option::help() const { return m_help; } Option::Type Option::type() const { return m_type; } int Option::flags() const { return m_flags; } /* -------------------------------------------------------- * OptionValue class * -------------------------------------------------------- */ OptionValue::OptionValue(const std::string &name) : m_name(name) , m_enabled(true) { } OptionValue::OptionValue(const std::string &name, const std::string &value) : m_name(name) , m_value(value) { } const std::string &OptionValue::name() const { return m_name; } const std::string &OptionValue::value() const { return m_value; } OptionValue::operator bool() const { return m_enabled; } OptionValue::operator std::string() const { return m_value; } /* -------------------------------------------------------- * OptionResult class * -------------------------------------------------------- */ auto OptionResult::begin() -> decltype(m_values.begin()) { return m_values.begin(); } auto OptionResult::begin() const -> decltype(m_values.begin()) { return m_values.begin(); } auto OptionResult::cbegin() const -> decltype(m_values.cbegin()) { return m_values.begin(); } auto OptionResult::end() -> decltype(m_values.end()) { return m_values.end(); } auto OptionResult::end() const -> decltype(m_values.end()) { return m_values.end(); } auto OptionResult::cend() const -> decltype(m_values.cend()) { return m_values.cend(); } int OptionResult::count() const { return static_cast<int>(m_values.size()); } int OptionResult::total() const { return m_total; } /* -------------------------------------------------------- * OptionParser class * -------------------------------------------------------- */ OptionParser::Args OptionParser::unpack(const Args &args) const { if (m_style != Getopt) return args; Args ret; for (const auto &s : args) { auto length = s.length(); if (length < 2 || s[0] != '-' || s[1] == '-') { ret.push_back(s); continue; } for (int i = 1; i < (int)length; ++i) { ret.push_back(std::string("-") + std::string(&s[i], 1)); } } return ret; } OptionParser::OptionParser(Style style) : m_style(style) { } void OptionParser::add(Option &&option) { const auto &length = option.m_name.size(); if (length > m_maxlength) m_maxlength = length; /* * If style is Getopt, we should use a double dash as the long option * like --color and a single for short options like -v */ if (m_style == Getopt) { if (length > 1) option.m_token = "--" + option.m_name; else option.m_token = "-" + option.m_name; } else option.m_token = "/" + option.m_name; m_options.emplace(std::make_pair(option.m_token, std::move(option))); } void OptionParser::add(const Option &option) { add(Option(option)); } OptionResult OptionParser::parse(int argc, const char * const *argv, int flags) { Args args; for (int i = 0; i < argc; ++i) args.push_back(argv[i]); return parse(args, flags); } OptionResult OptionParser::parse(const Args &argslist, int flags) { OptionResult result; int i; auto args = unpack(argslist); auto length = args.size(); for (i = 0; i < (int)length; ++i) { /* Not a valid option at all? */ if (m_options.count(args[i]) == 0) { /* When breaking is enabled we should not emit a warning */ if (flags & Strict) { if (!(flags & Silent)) { log(args[i], args[i] + " is not a valid option"); usage(); } break; } else if (flags & BreakNonOption) break; else continue; } /* * At this step, we have an option name valid, check if it requires * an argument or not. */ const auto &option = m_options.at(args[i]); const auto &name = option.name(); auto type = option.type(); /* * Already present and must appears only once? */ if ((option.flags() & Option::Single) && m_placed.count(name) > 0) { if (!(flags & Silent)) { log(name, "option " + name + " must appear only once"); if (flags & Strict) { usage(); break; } } else if (flags & Strict) break; } /* * Need an argument or it is a simple switch ? */ if (type == Option::Type::Switch) result.m_values.push_back(OptionValue(name)); else { if (i + 1 >= (int)length) result.m_values.push_back(OptionValue(name, "")); else { result.m_values.push_back(OptionValue(name, args[i + 1])); ++ i; } } m_placed.insert(name); } result.m_total = i; return result; } void OptionParser::usage() { std::cout << "options available: " << std::endl; for (const auto &option : m_options) { std::cout << " " << std::setw(m_maxlength) << option.second.name(); std::cout << "\t" << option.second.help() << std::endl; } } void OptionParser::log(const std::string &, const std::string &error) { std::cout << error << std::endl; }