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