From 6f4d899c0bbfc10bad5e70db0cb982891b700359 Mon Sep 17 00:00:00 2001 From: Danny Robson Date: Tue, 26 Feb 2013 18:31:14 +1100 Subject: [PATCH] Imported libcmdopt option parsing --- Makefile.am | 2 + options.cpp | 537 +++++++++++++++++++++++++++++++++++++++ options.hpp | 323 +++++++++++++++++++++++ test/.gitignore | 3 + test/Makefile.am | 4 + test/options/success.cpp | 253 ++++++++++++++++++ 6 files changed, 1122 insertions(+) create mode 100644 options.cpp create mode 100644 options.hpp create mode 100644 test/options/success.cpp diff --git a/Makefile.am b/Makefile.am index b86f0816..e4a03a1f 100644 --- a/Makefile.am +++ b/Makefile.am @@ -85,6 +85,8 @@ UTIL_FILES = \ noise/fractal.hpp \ noise/lut.cpp \ noise/lut.hpp \ + options.cpp \ + options.hpp \ platform.hpp \ point.cpp \ point.hpp \ diff --git a/options.cpp b/options.cpp new file mode 100644 index 00000000..a33d359d --- /dev/null +++ b/options.cpp @@ -0,0 +1,537 @@ +/* + * This file is part of libgim. + * + * libgim is free software: you can redistribute it and/or modify it under the + * terms of the GNU General Public License as published by the Free Software + * Foundation, either version 3 of the License, or (at your option) any later + * version. + * + * libgim is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License + * along with libgim. If not, see . + * + * Copyright 2013 Danny Robson + */ + + +#include "options.hpp" + +#include "config.h" + +#include +#include + +#include +#include +#include +#include +#include + +#include +#include + + +using namespace std; +using namespace util; + + +/* + * Generic option operations, failure or default modes + */ + + +option::option (char _letter, + const char *_name, + const char *_desc, + bool _required): + m_shortopt (_letter), + m_longopt (_name), + m_description(_desc), + m_required (_required), + m_found (false) +{ reset(); } + + + +void option::execute (void) { + throw runtime_error( + "Cannot provide no value for the option '" + m_longopt + "'" + ); +} + +void option::execute (const string& data) { + assert(data.size() > 0); + throw runtime_error( + "Cannot provide a value for the option '" + m_longopt + "'" + ); +} + + +ostream& operator<< (ostream & os, const option& opt) { + os << (opt.is_required () ? " -" : "[-" ) << opt.shortopt () + << (opt.is_required () ? " \t" : "]\t") << opt.longopt () + << "\t" << opt.description (); + return os; +} + + +void +option::finish (void) { + if (m_required && !m_found) + throw runtime_error ("Required argument not found: " + m_longopt); +} + + +/* + * Nulloption + */ + +nulloption::nulloption (char _letter, + const char *_name, + const char *_desc, + bool _required): + option (_letter, _name, _desc, _required) +{ ; } + + +/* + * Present option + */ + + +presentoption::presentoption (char _letter, + const char *_name, + const char *_desc, + bool *_data, + bool _required): + option (_letter, _name, _desc, _required), + m_data (_data) +{ ; } + + +void +presentoption::execute (void) { + *m_data = true; + m_found = true; +} + + +/* + * bytesoption + */ + +bytesoption::bytesoption (char _letter, + const char *_name, + const char *_desc, + size_t *_data, + bytestype _type, + bytesmodifier _modifier, + bool _required): + valueoption (_letter, _name, _desc, _data), + m_type (_type), + m_modifier (_modifier) +{ ; } + + +bytesoption::bytestype +bytesoption::type_from_character (char c) { + switch (c) { + case 'e': + case 'E': + return BYTES_EXA; + + case 'p': + case 'P': + return BYTES_PETA; + + case 't': + case 'T': + return BYTES_TERA; + + case 'g': + case 'G': + return BYTES_GIGA; + + case 'm': + case 'M': + return BYTES_MEGA; + + case 'k': + case 'K': + return BYTES_KILO; + } + + throw domain_error("Invalid magnitude specifier"); +} + + +void +bytesoption::execute (const std::string& data) { + // We shouldn't get this far into processing with a zero sized argument, + // that's what the null data execute function is for. + assert (data.size () > 0); + + size_t defaultvalue = *m_data; + + + try { + bytesmodifier modifier = m_modifier; + off_t cursor = data.size () - 1; + + // Consume an optional trailing `byte' type + if (data[cursor] == 'B' || data[cursor] == 'b') { + if (--cursor < 0) + throw invalid_argument ("Size is too short"); + } + + // Check if we explicitly request base2 + if (data[cursor] == 'i') { + modifier = BYTES_BASE2; + if (--cursor < 0) + throw invalid_argument("Size is too short"); + } + + // Determine what constant factor is needed for the raw size + uint64_t multiplier = 1; + uint64_t modifier_factor; + switch (modifier) { + case BYTES_BASE2: + modifier_factor = 1024; + break; + + case BYTES_BASE10: + modifier_factor = 1000; + break; + + default: + abort (); + } + + // Find the difference in magnitude between what is desired, and what + // is specified by the user. Raise the multiplier by that magnitude. + bytestype specified = m_type; + try { + specified = type_from_character (data[cursor]); + // If the character is a digit, it just means the user skipped the + // size specifier, which is ok. + } catch (domain_error x) { + if (!isdigit (data[cursor])) + throw invalid_argument ("Not a size"); + + // Falsely increment the cursor if there's no size specifier... + ++cursor; + } + + // ... so that we can easily decrement the cursor without special logic + // after reading the specifiers. + --cursor; + assert (cursor >= 0); + + multiplier = pow ((double)modifier_factor, (int)specified); + get_arg (data.substr(0, cursor + 1), m_data); + *m_data *= multiplier; + } catch (...) { + // Ensure that we haven't nuked a default value due to a half + // completed calculation followed by an exception. + + *m_data = defaultvalue; + throw; + } + + m_found = true; +} + +/* + * Internal helper options. Print help and usage. + * A callback to the processor which triggers output of the help message. + * + * This should never be instanced by a user of the system. The processor will + * automatically adds this to its available options where needed. + */ +class helpoption : public option { + protected: + static const char HELP_CHARACTER; + static const char *HELP_NAME; + static const char *HELP_DESCRIPTION; + + processor * m_processor; + + public: + helpoption (processor * _processor): + option (HELP_CHARACTER, HELP_NAME, HELP_DESCRIPTION, false), + m_processor (_processor) + { ; } + + + virtual void execute (void); + virtual void execute (const std::string& data) + { option::execute (data); } +}; + + +const char helpoption::HELP_CHARACTER = 'h'; +const char *helpoption::HELP_NAME = "help"; +const char *helpoption::HELP_DESCRIPTION = +"display help and usage information"; + + +void helpoption::execute (void) { + m_processor->print_usage (); + exit (EXIT_SUCCESS); +} + + +/* + * Command line processing options + */ + +processor::processor () +{ add_option (new helpoption (this)); } + + +processor::~processor () { + for(auto i = m_options.begin(); i != m_options.end(); i++) + delete *i; +} + + +void processor::print_usage (void) { + cout << "Usage: " << m_command << " [options]" << endl; + + for(list