/* * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * * Copyright 2013 Danny Robson */ #include "cmdopt.hpp" #include "debug.hpp" #include #include using util::cmdopt::option::base; using util::cmdopt::option::bytes; using util::cmdopt::option::count; using util::cmdopt::option::null; using util::cmdopt::option::present; using util::cmdopt::option::value; using util::cmdopt::parser; /////////////////////////////////////////////////////////////////////////////// base::base (std::string _name): m_name (_name), m_required (false), m_seen (false) { ; } //----------------------------------------------------------------------------- base::~base () { ; } //----------------------------------------------------------------------------- void base::execute (void) { throw invalid_null (m_name); } //----------------------------------------------------------------------------- void base::execute (const char *restrict) { throw invalid_value (m_name); } //----------------------------------------------------------------------------- void base::start (void) { m_seen = false; } //----------------------------------------------------------------------------- void base::finish (void) { if (m_required && !m_seen) throw invalid_required (m_name); } //----------------------------------------------------------------------------- std::string base::name (void) const { return m_name; } //----------------------------------------------------------------------------- bool base::required (void) const { return m_required; } //----------------------------------------------------------------------------- bool base::required (bool _required) { return m_required = _required; } //----------------------------------------------------------------------------- bool base::seen (void) const { return m_seen; } //----------------------------------------------------------------------------- bool base::seen (bool _seen) { return m_seen = _seen; } /////////////////////////////////////////////////////////////////////////////// null::null (std::string _name): base (std::move (_name)) { ; } //----------------------------------------------------------------------------- void null::execute (void) { seen (true); } //----------------------------------------------------------------------------- void null::execute (const char *restrict) { seen (true); } /////////////////////////////////////////////////////////////////////////////// present::present (std::string _name, bool &_data): base (std::move (_name)), m_data (_data) { ; } //----------------------------------------------------------------------------- void present::execute (void) { seen (true); } //----------------------------------------------------------------------------- void present::finish (void) { m_data = seen (); base::finish (); } //----------------------------------------------------------------------------- namespace util { namespace cmdopt { namespace option { template class value; template class value; template class value; } } } /////////////////////////////////////////////////////////////////////////////// template count::count (std::string _name, T &_data): value (std::move (_name), _data) { ; } //------------------------------------------------------------------------- template void count::execute (void) { ++this->data (); this->seen (true); } //----------------------------------------------------------------------------- namespace util { namespace cmdopt { namespace option { template class count; } } } /////////////////////////////////////////////////////////////////////////////// int parser::scan (int argc, char *const *argv) { CHECK_GE (argc, 0); CHECK (argv); for (auto &j: m_options) j->start (); // start iterating after our program's name int i = 1; while (i < argc) { auto len = strlen (argv[i]); // bail if there's no potential for an option if (len < 2 || argv[i][0] != '-') return i; // parse longopt auto inc = argv[i][1] == '-' ? parse_long (i, argc, argv) : parse_short (i, argc, argv); CHECK_GT (inc, 0); i += inc; } for (auto &j: m_options) j->finish (); return i; } //----------------------------------------------------------------------------- int parser::parse_long (int pos, int argc, char *const *argv) { CHECK_LT (pos, argc); CHECK_GE (pos, 0); CHECK_GE (argc, 0); CHECK (argv); CHECK_EQ (argv[pos][0], '-'); CHECK_EQ (argv[pos][1], '-'); // break first atom into components and extract the key const char *start = argv[pos] + 2; const char *eq = strchr (start, '='); const char *last = start + strlen (start); std::string key { start, eq ? eq : last }; // find the handler auto handle_pos = std::find_if (m_long.begin (), m_long.end (), [&] (auto i) { return std::get<0> (i) == key; }); if (handle_pos == m_long.end ()) throw invalid_key (key); auto &handler = std::get<1> (*handle_pos); // maybe grab a value from the next atom and dispatch if (!eq) { // check the next atom for the value if (pos + 1 < argc) if (argv[pos + 1][0] != '-') { handler.execute (argv[pos+1]); return 2; } handler.execute (); } else { handler.execute (eq+1); } return 1; } //----------------------------------------------------------------------------- int parser::parse_short (int pos, int argc, char *const *argv) { CHECK_LT (pos, argc); CHECK_GE (pos, 0); CHECK_GE (argc, 0); CHECK (argv); CHECK_EQ (argv[pos][0], '-'); CHECK_NEQ (argv[pos][1], '-'); // we have a run of no-value keys auto len = strlen (argv[pos]); if (len > 2 || pos + 1 == argc || argv[pos+1][0] == '-') { for (size_t i = 1; i < len; ++i) { auto letter = argv[pos][i]; auto hpos = std::find_if (m_short.begin (), m_short.end (), [letter] (auto j) { return std::get<0> (j) == letter; }); if (hpos == m_short.end ()) throw invalid_key (std::to_string (letter)); std::get<1> (*hpos).execute (); } return 1; } // we have a value following auto letter = argv[pos][1]; auto hpos = std::find_if (m_short.begin (), m_short.end (), [letter] (auto i) { return std::get<0> (i) == letter; }); if (hpos == m_short.end ()) throw invalid_key (std::to_string (letter)); std::get<1> (*hpos).execute (argv[pos+1]); return 2; }