- a whole lot of supplementary source code!!

Wed, 10 Dec 2014 19:17:00 +0200

author
Teemu Piippo <crimsondusk64@gmail.com>
date
Wed, 10 Dec 2014 19:17:00 +0200
changeset 1
4dd5bde4e777
parent 0
792876306489
child 2
8be472cb0bca

- a whole lot of supplementary source code!!

.hgignore file | annotate | diff | comparison | revisions
CMakeLists.txt file | annotate | diff | comparison | revisions
sources/basics.h file | annotate | diff | comparison | revisions
sources/filesystem.cpp file | annotate | diff | comparison | revisions
sources/filesystem.h file | annotate | diff | comparison | revisions
sources/format.cpp file | annotate | diff | comparison | revisions
sources/format.h file | annotate | diff | comparison | revisions
sources/geometry.h file | annotate | diff | comparison | revisions
sources/list.h file | annotate | diff | comparison | revisions
sources/main.cpp file | annotate | diff | comparison | revisions
sources/main.h file | annotate | diff | comparison | revisions
sources/mystring.cpp file | annotate | diff | comparison | revisions
sources/mystring.h file | annotate | diff | comparison | revisions
sources/range.h file | annotate | diff | comparison | revisions
sources/version.cpp file | annotate | diff | comparison | revisions
sources/version.h file | annotate | diff | comparison | revisions
updaterevision/CMakeLists.txt file | annotate | diff | comparison | revisions
updaterevision/updaterevision.py file | annotate | diff | comparison | revisions
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/.hgignore	Wed Dec 10 19:17:00 2014 +0200
@@ -0,0 +1,10 @@
+syntax:glob
+build
+debug
+release
+build_debug
+build_release
+*.kdev4
+CMakeLists.txt.user
+*.orig
+*.rej
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/CMakeLists.txt	Wed Dec 10 19:17:00 2014 +0200
@@ -0,0 +1,24 @@
+cmake_minimum_required (VERSION 2.4)
+project (hurgharama)
+
+set (SOURCE_FILES
+	sources/filesystem.cpp
+	sources/format.cpp
+	sources/main.cpp
+	sources/mystring.cpp
+	sources/version.cpp
+)
+
+set (CURSES_NEED_NCURSES, True)
+set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++0x -W -Wall")
+find_package (Curses)
+include_directories (${CURSES_INCUDE_DIRS})
+include_directories (${CMAKE_CURRENT_BINARY_DIR})
+add_executable (${PROJECT_NAME} ${SOURCE_FILES})
+target_link_libraries (${PROJECT_NAME} ${CURSES_LIBRARIES})
+
+add_custom_target (make_hginfo_h
+	COMMAND python
+		"${CMAKE_SOURCE_DIR}/updaterevision/updaterevision.py"
+		"${CMAKE_CURRENT_BINARY_DIR}/hginfo.h")
+add_dependencies (${PROJECT_NAME} make_hginfo_h)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/sources/basics.h	Wed Dec 10 19:17:00 2014 +0200
@@ -0,0 +1,58 @@
+/*
+	Copyright 2014 Teemu Piippo
+	All rights reserved.
+
+	Redistribution and use in source and binary forms, with or without
+	modification, are permitted provided that the following conditions
+	are met:
+
+	1. Redistributions of source code must retain the above copyright
+	   notice, this list of conditions and the following disclaimer.
+	2. Redistributions in binary form must reproduce the above copyright
+	   notice, this list of conditions and the following disclaimer in the
+	   documentation and/or other materials provided with the distribution.
+	3. Neither the name of the copyright holder nor the names of its
+	   contributors may be used to endorse or promote products derived from
+	   this software without specific prior written permission.
+
+	THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+	"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+	TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+	PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER
+	OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+	EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+	PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+	PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+	LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+	NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+	SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#pragma once
+#include <algorithm>
+#include <functional>
+
+#define FUNCTION auto
+#define METHOD auto
+#define MACRO_TO_STRING_2(A) #A
+#define MACRO_TO_STRING(A) MACRO_TO_STRING_2(A)
+
+using std::swap;
+
+enum Color
+{
+	BLACK,
+	RED,
+	GREEN,
+	YELLOW,
+	BLUE,
+	MAGENTA,
+	CYAN,
+	WHITE,
+	DEFAULT,
+
+	NUM_COLORS
+};
+
+template<typename Signature>
+using Function = std::function<Signature>;
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/sources/filesystem.cpp	Wed Dec 10 19:17:00 2014 +0200
@@ -0,0 +1,68 @@
+/*
+	Copyright 2014 Teemu Piippo
+	All rights reserved.
+
+	Redistribution and use in source and binary forms, with or without
+	modification, are permitted provided that the following conditions
+	are met:
+
+	1. Redistributions of source code must retain the above copyright
+	   notice, this list of conditions and the following disclaimer.
+	2. Redistributions in binary form must reproduce the above copyright
+	   notice, this list of conditions and the following disclaimer in the
+	   documentation and/or other materials provided with the distribution.
+	3. Neither the name of the copyright holder nor the names of its
+	   contributors may be used to endorse or promote products derived from
+	   this software without specific prior written permission.
+
+	THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+	"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+	TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+	PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER
+	OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+	EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+	PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+	PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+	LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+	NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+	SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#include "filesystem.h"
+
+#ifndef _WIN32
+# define DIRSLASH "/"
+#else
+# define DIRSLASH "\\"
+#endif
+
+// -------------------------------------------------------------------------------------------------
+
+FUNCTION
+dirname (String const& path) -> String
+{
+	int lastpos = path.find_last (DIRSLASH);
+
+	if (lastpos > 0)
+		return path.mid (0, lastpos);
+
+#ifndef _WIN32
+	if (path[0] == '/')
+		return "/";
+#endif // _WIN32
+
+	return "";
+}
+
+// -------------------------------------------------------------------------------------------------
+
+FUNCTION
+basename (String const& path) -> String
+{
+	long lastpos = path.find_last (DIRSLASH);
+
+	if (lastpos != -1)
+		return path.mid (lastpos + 1);
+
+	return path;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/sources/filesystem.h	Wed Dec 10 19:17:00 2014 +0200
@@ -0,0 +1,35 @@
+/*
+	Copyright 2014 Teemu Piippo
+	All rights reserved.
+
+	Redistribution and use in source and binary forms, with or without
+	modification, are permitted provided that the following conditions
+	are met:
+
+	1. Redistributions of source code must retain the above copyright
+	   notice, this list of conditions and the following disclaimer.
+	2. Redistributions in binary form must reproduce the above copyright
+	   notice, this list of conditions and the following disclaimer in the
+	   documentation and/or other materials provided with the distribution.
+	3. Neither the name of the copyright holder nor the names of its
+	   contributors may be used to endorse or promote products derived from
+	   this software without specific prior written permission.
+
+	THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+	"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+	TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+	PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER
+	OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+	EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+	PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+	PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+	LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+	NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+	SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#pragma once
+#include "main.h"
+
+FUNCTION basename (String const& path) -> String;
+FUNCTION dirname (String const& path) -> String;
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/sources/format.cpp	Wed Dec 10 19:17:00 2014 +0200
@@ -0,0 +1,107 @@
+/*
+	Copyright 2014 Teemu Piippo
+	All rights reserved.
+
+	Redistribution and use in source and binary forms, with or without
+	modification, are permitted provided that the following conditions
+	are met:
+
+	1. Redistributions of source code must retain the above copyright
+	   notice, this list of conditions and the following disclaimer.
+	2. Redistributions in binary form must reproduce the above copyright
+	   notice, this list of conditions and the following disclaimer in the
+	   documentation and/or other materials provided with the distribution.
+	3. Neither the name of the copyright holder nor the names of its
+	   contributors may be used to endorse or promote products derived from
+	   this software without specific prior written permission.
+
+	THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+	"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+	TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+	PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER
+	OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+	EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+	PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+	PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+	LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+	NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+	SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#include <cstdio>
+#include "format.h"
+
+//
+// Throws an error while formatting the string
+//
+static auto format_error (String fmtstr, const String errdescribe, int pos) -> void
+{
+	fmtstr.replace ("\n", " ");
+	fmtstr.replace ("\t", " ");
+	String errmsg ("With format string:\n" + fmtstr + "\n");
+
+	for (int x = 0; x < pos; ++x)
+		errmsg += "-";
+
+	errmsg += "^\n" + errdescribe;
+	// throw std::logic_error (errmsg.stdString());
+	std::fprintf (stderr, "%s", errmsg.chars());
+	std::exit (EXIT_FAILURE);
+}
+
+//
+// Main formatter algorithm autoion. Processes @fmtstr with @args and returns
+// the result.
+//
+auto format_args (const String& fmtstr, const Vector<String>& args) -> String
+{
+	String fmt = fmtstr;
+	int pos = 0;
+
+	while ((pos = fmt.find ("%", pos)) != -1)
+	{
+		if (fmt[pos + 1] == '%')
+		{
+			fmt.replace (pos, 2, "%");
+			pos++;
+			continue;
+		}
+
+		int ofs = 1;
+		char mod = '\0';
+
+		// handle modifiers
+		if (fmt[pos + ofs] == 's' or fmt[pos + ofs] == 'x' or fmt[pos + ofs] == 'd')
+		{
+			mod = fmt[pos + ofs];
+			ofs++;
+		}
+
+		if (fmt[pos + ofs] < '1' or fmt[pos + ofs] > '9')
+		{
+			format_error (fmtstr, "bad format string, expected non-zero digit with "
+				"optional modifier after '%%'", pos);
+		}
+
+		int i = fmt[pos + ofs] - '0';
+
+		if (i > args.size())
+			format_error (fmtstr, String ("Format argument #") + i + " used but not defined.", pos);
+
+		String replacement = args[i - 1];
+
+		switch (mod)
+		{
+		case 's': replacement = (replacement == "1") ? "" : "s";      break;
+		case 'd': replacement.sprintf ("%d", replacement[0]);         break;
+		case 'x': replacement.sprintf ("0x%x", replacement.to_int()); break;
+		default: break;
+		}
+
+		fmt.replace (pos, 1 + ofs, replacement);
+		pos += replacement.length();
+	}
+
+	return fmt;
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/sources/format.h	Wed Dec 10 19:17:00 2014 +0200
@@ -0,0 +1,216 @@
+/*
+	Copyright 2014 Teemu Piippo
+	All rights reserved.
+
+	Redistribution and use in source and binary forms, with or without
+	modification, are permitted provided that the following conditions
+	are met:
+
+	1. Redistributions of source code must retain the above copyright
+	   notice, this list of conditions and the following disclaimer.
+	2. Redistributions in binary form must reproduce the above copyright
+	   notice, this list of conditions and the following disclaimer in the
+	   documentation and/or other materials provided with the distribution.
+	3. Neither the name of the copyright holder nor the names of its
+	   contributors may be used to endorse or promote products derived from
+	   this software without specific prior written permission.
+
+	THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+	"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+	TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+	PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER
+	OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+	EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+	PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+	PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+	LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+	NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+	SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#include "mystring.h"
+#include "basics.h"
+#include "geometry.h"
+
+#define FORMAT_OVERLOAD(T) \
+	inline FUNCTION make_format_argument (T a) -> String
+
+//
+// -------------------------------------------------------------------------------------------------
+//
+
+FORMAT_OVERLOAD (String) { return a; }
+FORMAT_OVERLOAD (char) { return String (a); }
+FORMAT_OVERLOAD (int) { return String::from_number (a); }
+FORMAT_OVERLOAD (long int) { return String::from_number (a); }
+FORMAT_OVERLOAD (double) { return String::from_number (a); }
+FORMAT_OVERLOAD (size_t) { return String::from_number (a); }
+FORMAT_OVERLOAD (const char*) { return a; }
+FORMAT_OVERLOAD (std::nullptr_t) { (void) a; return "<null pointer>"; }
+FORMAT_OVERLOAD (bool) { return a ? "true" : "false"; }
+
+FORMAT_OVERLOAD (const void*)
+{
+	String result;
+	result.sprintf ("%p", a);
+	return result;
+}
+
+template<typename T>
+FORMAT_OVERLOAD (const List<T>&)
+{
+	String result;
+
+	if (a.is_empty())
+		return "{}";
+
+	result = "{";
+
+	for (auto it = a.begin(); it != a.end(); ++it)
+	{
+		if (it != a.begin())
+			result += ", ";
+
+		result += make_format_argument (*it);
+	}
+
+	result += "}";
+	return result;
+}
+
+FORMAT_OVERLOAD (Color)
+{
+	static const char* colorstrings[] =
+	{
+		"BLACK", "RED", "GREEN", "YELLOW",
+		"BLUE", "MAGENTA", "CYAN", "WHITE", "DEFAULT"
+	};
+
+	static_assert (sizeof colorstrings / sizeof *colorstrings == NUM_COLORS,
+		"colostrings[] has an incorrect amount of elements");
+
+	if (int (a) >= 0 and int (a) < NUM_COLORS)
+		return colorstrings[int (a)];
+
+	return "???";
+}
+
+FORMAT_OVERLOAD (Position) { return String ("(") + a.x + ", " + a.y + ")"; }
+FORMAT_OVERLOAD (Size) { return String ("(") + a.width + "x" + a.height + ")"; }
+
+FORMAT_OVERLOAD (Rectangle)
+{
+	String result;
+	result.sprintf ("{(%d, %d), (%dx%d)}", a.x, a.y, a.width, a.height);
+	return result;
+}
+
+//
+// -------------------------------------------------------------------------------------------------
+//
+// Formats the given string with the given args.
+//
+auto format_args (const String& fmtstr, const Vector<String>& args) -> String;
+
+//
+// -------------------------------------------------------------------------------------------------
+//
+// Expands the given arguments into a vector of strings.
+//
+template<typename T, typename... RestTypes>
+auto expand_format_arguments (Vector<String>& data, const T& arg, const RestTypes& ... rest) -> void
+{
+	data.append (make_format_argument (arg).text());
+	expand_format_arguments (data, rest...);
+}
+
+static void expand_format_arguments (Vector<String>&) __attribute__( (unused));
+static void expand_format_arguments (Vector<String>&) {}
+
+//
+// -------------------------------------------------------------------------------------------------
+//
+// Formats the given formatter string and args and returns the string.
+// This is essentially a modernized sprintf.
+//
+// Args in the format string take the form %n where n is a digit. The argument
+// will be expanded to the nth argument passed. This is essentially Qt's
+// QString::arg() syntax. Note: %0 is invalid.
+//
+// Arguments can be passed a modifier which takes the form of a character
+// just before the digit. Currently supported modifiers are s, d and x.
+//
+// - s: The argument will expand into "s" if it would've not expanded into "1"
+//      otherwise. If it would have expanded into "1" it will expand into an
+//      empty string.
+//
+// - d: The argument expands into the numeric form of the first character of
+//      its previous expansion. Use this to get numeric forms of @c char
+//      arguments.
+//
+// - x: The numeric argument will be represented in hexadecimal notation. This
+//      will work if the argument is a string representing a number. If the
+//      argument did not expand into a number in the first place, 0 is used
+//      and 0x0 is printed.
+//
+template<typename... argtypes>
+String format (const String& fmtstr, const argtypes&... raw_args)
+{
+	Vector<String> args;
+	expand_format_arguments (args, raw_args...);
+	return format_args (fmtstr, args);
+}
+
+//
+// -------------------------------------------------------------------------------------------------
+//
+// This is an overload of format() where no arguments are supplied.
+// It returns the formatter string as-is.
+//
+static String format (const String& fmtstr) __attribute__ ((unused));
+static String format (const String& fmtstr)
+{
+	return fmtstr;
+}
+
+//
+// -------------------------------------------------------------------------------------------------
+//
+
+template<typename... Args>
+void print_to (FILE* fp, const String& fmtstr, Args const& ...args)
+{
+	std::fprintf (fp, "%s", format (fmtstr, args...).chars());
+}
+
+//
+// -------------------------------------------------------------------------------------------------
+//
+
+template<typename... argtypes>
+void print_to (const String& filename, const String& fmtstr, const argtypes&... args)
+{
+	FILE* handle;
+
+	if ((handle = fopen (filename, "w+")))
+	{
+		print_to (handle, fmtstr, args...);
+		fclose (handle);
+	}
+}
+
+//
+// -------------------------------------------------------------------------------------------------
+//
+
+template<typename... argtypes>
+void print (const String& fmtstr, const argtypes&... args)
+{
+	print_to (stdout, fmtstr, args...);
+}
+
+#define PRINT_TO_LOG(...) \
+{ \
+	print_to (LOGFILE, "%1:%2: ", __PRETTY_FUNCTION__, __LINE__); \
+	print_to (LOGFILE, __VA_ARGS__); \
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/sources/geometry.h	Wed Dec 10 19:17:00 2014 +0200
@@ -0,0 +1,140 @@
+/*
+	Copyright 2014 Teemu Piippo
+	All rights reserved.
+
+	Redistribution and use in source and binary forms, with or without
+	modification, are permitted provided that the following conditions
+	are met:
+
+	1. Redistributions of source code must retain the above copyright
+	   notice, this list of conditions and the following disclaimer.
+	2. Redistributions in binary form must reproduce the above copyright
+	   notice, this list of conditions and the following disclaimer in the
+	   documentation and/or other materials provided with the distribution.
+	3. Neither the name of the copyright holder nor the names of its
+	   contributors may be used to endorse or promote products derived from
+	   this software without specific prior written permission.
+
+	THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+	"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+	TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+	PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER
+	OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+	EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+	PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+	PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+	LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+	NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+	SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#pragma once
+
+struct Position
+{
+	int x;
+	int y;
+
+	Position (int x, int y) :
+		x (x),
+		y (y) {}
+
+	Position() :
+		x (0),
+		y (0) {}
+
+	inline METHOD operator< (const Position& other) const -> bool;
+	inline METHOD operator> (const Position& other) const -> bool;
+	inline METHOD operator== (const Position& other) const -> bool;
+	inline METHOD operator<= (const Position& other) const -> bool;
+	inline METHOD operator>= (const Position& other) const -> bool;
+	inline METHOD operator!= (const Position& other) const -> bool;
+};
+
+// -------------------------------------------------------------------------------------------------
+
+struct Size
+{
+	int width;
+	int height;
+
+	Size (int width, int height) :
+		width (width),
+		height (height) {}
+
+	Size() :
+		width (0),
+		height (0) {}
+
+	auto area() const -> int
+	{
+		return width * height;
+	}
+};
+
+// -------------------------------------------------------------------------------------------------
+
+struct Rectangle : Position, Size
+{
+	Rectangle (int x, int y, int width, int height) :
+		Position (x, y),
+		Size (width, height) {}
+
+	Rectangle() :
+		Position(),
+		Size() {}
+};
+
+// -------------------------------------------------------------------------------------------------
+
+inline METHOD
+Position::operator< (const Position& other) const -> bool
+{
+	if (y != other.y)
+		return y < other.y;
+
+	return x < other.x;
+}
+
+// -------------------------------------------------------------------------------------------------
+
+inline METHOD
+Position::operator> (const Position& other) const -> bool
+{
+	if (y != other.y)
+		return y > other.y;
+
+	return x > other.x;
+}
+
+// -------------------------------------------------------------------------------------------------
+
+inline METHOD
+Position::operator== (const Position& other) const -> bool
+{
+	return y == other.y and x == other.x;
+}
+
+// -------------------------------------------------------------------------------------------------
+
+inline METHOD
+Position::operator<= (const Position& other) const -> bool
+{
+	return not operator> (other);
+}
+
+// -------------------------------------------------------------------------------------------------
+
+inline METHOD
+Position::operator>= (const Position& other) const -> bool
+{
+	return not operator< (other);
+}
+
+// -------------------------------------------------------------------------------------------------
+
+inline METHOD
+Position::operator!= (const Position& other) const -> bool
+{
+	return not operator== (other);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/sources/list.h	Wed Dec 10 19:17:00 2014 +0200
@@ -0,0 +1,609 @@
+/*
+	Copyright 2014 Teemu Piippo
+	All rights reserved.
+
+	Redistribution and use in source and binary forms, with or without
+	modification, are permitted provided that the following conditions
+	are met:
+
+	1. Redistributions of source code must retain the above copyright
+	   notice, this list of conditions and the following disclaimer.
+	2. Redistributions in binary form must reproduce the above copyright
+	   notice, this list of conditions and the following disclaimer in the
+	   documentation and/or other materials provided with the distribution.
+	3. Neither the name of the copyright holder nor the names of its
+	   contributors may be used to endorse or promote products derived from
+	   this software without specific prior written permission.
+
+	THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+	"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+	TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+	PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER
+	OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+	EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+	PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+	PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+	LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+	NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+	SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#pragma once
+#include "basics.h"
+#include <algorithm>
+#include <deque>
+#include <initializer_list>
+#include <functional>
+#include <cassert>
+#include "range.h"
+
+//
+// -------------------------------------------------------------------------------------------------
+//
+
+template<typename T, typename C>
+class Container
+{
+public:
+	using Iterator = typename C::iterator;
+	using ConstIterator = typename C::const_iterator;
+	using ReverseIterator = typename C::reverse_iterator;
+	using ConstReverseIterator = typename C::const_reverse_iterator;
+	using Self = Container<T, C>;
+
+	Container();
+	Container (int numvalues);
+	Container (const C& a);
+	Container (std::initializer_list<T>&& a);
+
+	auto append (const T& value) -> T&;
+	auto begin() -> Iterator;
+	auto begin() const -> ConstIterator;
+	auto clear() -> void;
+	auto contains (const T& a) const -> bool;
+	auto crbegin() const -> ConstReverseIterator;
+	auto crend() const -> ConstReverseIterator;
+	auto container() const -> const C&;
+	auto end() -> Iterator;
+	auto end() const -> ConstIterator;
+	auto find (const T& needle) -> Iterator;
+	auto find (const T& needle) const -> ConstIterator;
+	auto find (Function<bool (T const&)> func) -> Iterator;
+	auto find (Function<bool (T const&)> func) const -> ConstIterator;
+	auto first() const -> const T&;
+	auto insert (int pos, const T& value) -> void;
+	auto is_empty() const -> bool;
+	auto last() const -> const T&;
+	auto merge (const Self& other) -> void;
+	auto pop (T& val) -> bool;
+	auto prepend (const T& value) -> T&;
+	auto rbegin() -> ReverseIterator;
+	auto remove_at (int pos) -> void;
+	auto remove_duplicates() -> void;
+	auto remove_one (const T& it) -> void;
+	auto rend() -> ReverseIterator;
+	auto resize (int size) -> void;
+	auto reverse() const -> Self;
+	auto size() const -> int;
+	auto sort() -> void;
+	auto splice (int a, int b) const -> Self;
+	auto splice (const Range<int>& a) const -> Self;
+
+	auto operator<< (const T& value) -> Self&;
+	auto operator<< (const Self& vals) -> Self&;
+	auto operator[] (int n) -> T&;
+	auto operator[] (int n) const -> const T&;
+	auto operator[] (Range<int> const& n) const -> Self;
+	auto operator+ (const Self& other) const -> Self;
+
+protected:
+	C m_container;
+};
+
+template<typename T, typename C>
+Container<T, C>& operator>> (const T& value, Container<T, C>& haystack);
+
+template<typename T>
+using List = Container<T, std::deque<T>>;
+
+template<typename T>
+class Vector : public Container<T, std::vector<T>>
+{
+public:
+	using Super = Container<T, std::vector<T>>;
+
+	template<typename... Args>
+	Vector (Args ...args) :
+		Super (args...) {}
+
+	auto data() -> T*
+	{
+		return Super::m_container.data();
+	}
+
+	auto data() const -> const T*
+	{
+		return Super::m_container.data();
+	}
+
+	operator const T*() const
+	{
+		return data();
+	}
+};
+
+//
+// -------------------------------------------------------------------------------------------------
+//
+// IMPLEMENTATIONS
+//
+
+template<typename T, typename C>
+Container<T, C>::Container() {}
+
+//
+// -------------------------------------------------------------------------------------------------
+//
+
+template<typename T, typename C>
+Container<T, C>::Container (const C& other) :
+	m_container (other) {}
+
+//
+// -------------------------------------------------------------------------------------------------
+//
+
+template<typename T, typename C>
+Container<T, C>::Container (std::initializer_list<T> && a) :
+	m_container (a) {}
+
+//
+// -------------------------------------------------------------------------------------------------
+//
+
+template<typename T, typename C>
+Container<T, C>::Container (int numvalues) :
+	m_container (numvalues) {}
+
+//
+// -------------------------------------------------------------------------------------------------
+//
+
+template<typename T, typename C>
+auto Container<T, C>::begin() -> Iterator
+{
+	return m_container.begin();
+}
+
+//
+// -------------------------------------------------------------------------------------------------
+//
+
+template<typename T, typename C>
+auto Container<T, C>::begin() const -> ConstIterator
+{
+	return m_container.cbegin();
+}
+
+//
+// -------------------------------------------------------------------------------------------------
+//
+
+template<typename T, typename C>
+auto Container<T, C>::end() -> Iterator
+{
+	return m_container.end();
+}
+
+//
+// -------------------------------------------------------------------------------------------------
+//
+
+template<typename T, typename C>
+auto Container<T, C>::end() const -> ConstIterator
+{
+	return m_container.cend();
+}
+
+//
+// -------------------------------------------------------------------------------------------------
+//
+
+template<typename T, typename C>
+auto Container<T, C>::rbegin() -> ReverseIterator
+{
+	return m_container.rbegin();
+}
+
+//
+// -------------------------------------------------------------------------------------------------
+//
+
+template<typename T, typename C>
+auto Container<T, C>::crbegin() const -> ConstReverseIterator
+{
+	return m_container.crbegin();
+}
+
+//
+// -------------------------------------------------------------------------------------------------
+//
+
+template<typename T, typename C>
+auto Container<T, C>::rend() -> ReverseIterator
+{
+	return m_container.rend();
+}
+
+//
+// -------------------------------------------------------------------------------------------------
+//
+
+template<typename T, typename C>
+auto Container<T, C>::crend() const -> ConstReverseIterator
+{
+	return m_container.crend();
+}
+
+//
+// -------------------------------------------------------------------------------------------------
+//
+
+template<typename T, typename C>
+auto Container<T, C>::remove_at (int pos) -> void
+{
+	assert (pos < size());
+	m_container.erase (m_container.begin() + pos);
+}
+
+//
+// -------------------------------------------------------------------------------------------------
+//
+
+template<typename T, typename C>
+auto Container<T, C>::prepend (const T& value) -> T&
+{
+	m_container.push_front (value);
+	return m_container[0];
+}
+
+//
+// -------------------------------------------------------------------------------------------------
+//
+
+template<typename T, typename C>
+auto Container<T, C>::append (const T& value) -> T&
+{
+	m_container.push_back (value);
+	return m_container[m_container.size() - 1];
+}
+
+//
+// -------------------------------------------------------------------------------------------------
+//
+
+template<typename T, typename C>
+auto Container<T, C>::merge (const Self& other) -> void
+{
+	int oldsize = size();
+	resize (size() + other.size());
+	std::copy (other.begin(), other.end(), begin() + oldsize);
+}
+
+//
+// -------------------------------------------------------------------------------------------------
+//
+
+template<typename T, typename C>
+auto Container<T, C>::pop (T& val) -> bool
+{
+	if (is_empty())
+		return false;
+
+	val = m_container[size() - 1];
+	m_container.erase (m_container.end() - 1);
+	return true;
+}
+
+//
+// -------------------------------------------------------------------------------------------------
+//
+
+template<typename T, typename C>
+auto Container<T, C>::operator<< (const T& value) -> Self&
+{
+	append (value);
+	return *this;
+}
+
+//
+// -------------------------------------------------------------------------------------------------
+//
+
+template<typename T, typename C>
+auto Container<T, C>::operator<< (const Self& vals) -> Self&
+{
+	merge (vals);
+	return *this;
+}
+
+//
+// -------------------------------------------------------------------------------------------------
+//
+
+template<typename T, typename C>
+auto Container<T, C>::reverse() const -> Self
+{
+	Self rev;
+	std::copy (rbegin(), rend(), rev.begin());
+	return rev;
+}
+
+//
+// -------------------------------------------------------------------------------------------------
+//
+
+template<typename T, typename C>
+auto Container<T, C>::clear() -> void
+{
+	m_container.clear();
+}
+
+//
+// -------------------------------------------------------------------------------------------------
+//
+
+template<typename T, typename C>
+auto Container<T, C>::insert (int pos, const T& value) -> void
+{
+	m_container.insert (m_container.begin() + pos, value);
+}
+
+//
+// -------------------------------------------------------------------------------------------------
+//
+
+template<typename T, typename C>
+auto Container<T, C>::remove_duplicates() -> void
+{
+	sort();
+	resize (std::distance (begin(), std::unique (begin(), end())));
+}
+
+//
+// -------------------------------------------------------------------------------------------------
+//
+
+template<typename T, typename C>
+auto Container<T, C>::size() const -> int
+{
+	return m_container.size();
+}
+
+//
+// -------------------------------------------------------------------------------------------------
+//
+
+template<typename T, typename C>
+auto Container<T, C>::operator[] (int n) -> T&
+{
+	assert (n < size());
+	return m_container[n];
+}
+
+//
+// -------------------------------------------------------------------------------------------------
+//
+
+template<typename T, typename C>
+auto Container<T, C>::operator[] (int n) const -> const T&
+{
+	assert (n < size());
+	return m_container[n];
+}
+
+//
+// -------------------------------------------------------------------------------------------------
+//
+
+template<typename T, typename C>
+auto Container<T, C>::operator[] (const Range<int>& n) const -> Self
+{
+	return splice (n);
+}
+
+//
+// -------------------------------------------------------------------------------------------------
+//
+
+template<typename T, typename C>
+auto Container<T, C>::resize (int size) -> void
+{
+	m_container.resize (size);
+}
+
+//
+// -------------------------------------------------------------------------------------------------
+//
+
+template<typename T, typename C>
+auto Container<T, C>::sort() -> void
+{
+	std::sort (begin(), end());
+}
+
+//
+// -------------------------------------------------------------------------------------------------
+//
+
+template<typename T, typename C>
+auto Container<T, C>::find (const T& needle) -> Iterator
+{
+	auto it = std::find (m_container.begin(), m_container.end(), needle);
+
+	if (it == m_container.end())
+		return end();
+
+	return it;
+}
+
+//
+// -------------------------------------------------------------------------------------------------
+//
+
+template<typename T, typename C>
+auto Container<T, C>::find (const T& needle) const -> ConstIterator
+{
+	auto it = std::find (m_container.cbegin(), m_container.cend(), needle);
+
+	if (it == m_container.cend())
+		return end();
+
+	return it;
+}
+
+//
+// -------------------------------------------------------------------------------------------------
+//
+
+template<typename T, typename C>
+auto Container<T, C>::find (Function<bool (T const&)> func) -> Iterator
+{
+	for (Iterator it = begin(); it != end(); ++it)
+	{
+		if (func (*it))
+			return it;
+	}
+
+	return end();
+}
+
+//
+// -------------------------------------------------------------------------------------------------
+//
+
+template<typename T, typename C>
+auto Container<T, C>::find (Function<bool (T const&)> func) const -> ConstIterator
+{
+	for (ConstIterator it = begin(); it != end(); ++it)
+	{
+		if (func (*it))
+			return it;
+	}
+
+	return end();
+}
+
+//
+// -------------------------------------------------------------------------------------------------
+//
+
+template<typename T, typename C>
+auto Container<T, C>::remove_one (const T& a) -> void
+{
+	auto it = std::find (m_container.begin(), m_container.end(), a);
+
+	if (it != m_container.end())
+		m_container.erase (it);
+}
+
+//
+// -------------------------------------------------------------------------------------------------
+//
+
+template<typename T, typename C>
+auto Container<T, C>::is_empty() const -> bool
+{
+	return size() == 0;
+}
+
+//
+// -------------------------------------------------------------------------------------------------
+//
+
+template<typename T, typename C>
+auto Container<T, C>::splice (int a, int b) const -> Self
+{
+	if (a < 0 or b >= size() or b < a)
+		return Self();
+
+	Self result;
+
+	for (int i = a; i <= b; ++i)
+		result << operator[] (i);
+
+	return result;
+}
+
+//
+// -------------------------------------------------------------------------------------------------
+//
+
+template<typename T, typename C>
+auto Container<T, C>::splice (const Range<int>& a) const -> Self
+{
+	return splice (a.min(), a.max());
+}
+
+//
+// -------------------------------------------------------------------------------------------------
+//
+
+template<typename T, typename C>
+auto Container<T, C>::container() const -> const C&
+{
+	return m_container;
+}
+
+//
+// -------------------------------------------------------------------------------------------------
+//
+
+template<typename T, typename C>
+auto Container<T, C>::first() const -> const T&
+{
+	return *m_container.cbegin();
+}
+
+//
+// -------------------------------------------------------------------------------------------------
+//
+
+template<typename T, typename C>
+auto Container<T, C>::last() const -> const T&
+{
+	return *(m_container.cend() - 1);
+}
+
+//
+// -------------------------------------------------------------------------------------------------
+//
+
+template<typename T, typename C>
+auto Container<T, C>::contains (const T& a) const -> bool
+{
+	return std::find (m_container.cbegin(), m_container.cend(), a) != m_container.end();
+}
+
+//
+// -------------------------------------------------------------------------------------------------
+//
+
+template<typename T, typename C>
+auto Container<T, C>::operator+ (const Self& other) const -> Self
+{
+	Self out (*this);
+	out.merge (other);
+	return out;
+}
+
+//
+// -------------------------------------------------------------------------------------------------
+//
+
+template<typename T, typename C>
+auto operator>> (const T& value, Container<T, C>& haystack) -> Container<T, C>&
+{
+	haystack.prepend (value);
+	return haystack;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/sources/main.cpp	Wed Dec 10 19:17:00 2014 +0200
@@ -0,0 +1,47 @@
+/*
+	Copyright 2014 Teemu Piippo
+	All rights reserved.
+
+	Redistribution and use in source and binary forms, with or without
+	modification, are permitted provided that the following conditions
+	are met:
+
+	1. Redistributions of source code must retain the above copyright
+	   notice, this list of conditions and the following disclaimer.
+	2. Redistributions in binary form must reproduce the above copyright
+	   notice, this list of conditions and the following disclaimer in the
+	   documentation and/or other materials provided with the distribution.
+	3. Neither the name of the copyright holder nor the names of its
+	   contributors may be used to endorse or promote products derived from
+	   this software without specific prior written permission.
+
+	THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+	"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+	TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+	PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER
+	OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+	EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+	PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+	PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+	LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+	NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+	SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#include "main.h"
+
+FUNCTION
+main (int argc, char* argv[]) -> int
+{
+	initscr();
+	start_color();
+	raw();
+	keypad (stdscr, true);
+	noecho();
+	refresh();
+	printw ("Hello, world from %s %s (%s)",
+		APPNAME, full_version_string(), changeset_date_string());
+	getch();
+	endwin();
+	return EXIT_SUCCESS;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/sources/main.h	Wed Dec 10 19:17:00 2014 +0200
@@ -0,0 +1,36 @@
+/*
+	Copyright 2014 Teemu Piippo
+	All rights reserved.
+
+	Redistribution and use in source and binary forms, with or without
+	modification, are permitted provided that the following conditions
+	are met:
+
+	1. Redistributions of source code must retain the above copyright
+	   notice, this list of conditions and the following disclaimer.
+	2. Redistributions in binary form must reproduce the above copyright
+	   notice, this list of conditions and the following disclaimer in the
+	   documentation and/or other materials provided with the distribution.
+	3. Neither the name of the copyright holder nor the names of its
+	   contributors may be used to endorse or promote products derived from
+	   this software without specific prior written permission.
+
+	THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+	"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+	TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+	PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER
+	OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+	EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+	PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+	PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+	LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+	NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+	SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#pragma once
+#include <ncurses.h>
+#include "basics.h"
+#include "mystring.h"
+#include "geometry.h"
+#include "version.h"
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/sources/mystring.cpp	Wed Dec 10 19:17:00 2014 +0200
@@ -0,0 +1,482 @@
+/*
+	Copyright 2014 Teemu Piippo
+	All rights reserved.
+
+	Redistribution and use in source and binary forms, with or without
+	modification, are permitted provided that the following conditions
+	are met:
+
+	1. Redistributions of source code must retain the above copyright
+	   notice, this list of conditions and the following disclaimer.
+	2. Redistributions in binary form must reproduce the above copyright
+	   notice, this list of conditions and the following disclaimer in the
+	   documentation and/or other materials provided with the distribution.
+	3. Neither the name of the copyright holder nor the names of its
+	   contributors may be used to endorse or promote products derived from
+	   this software without specific prior written permission.
+
+	THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+	"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+	TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+	PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER
+	OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+	EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+	PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+	PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+	LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+	NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+	SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#include <cstring>
+#include "main.h"
+#include "mystring.h"
+#include "format.h"
+
+// -------------------------------------------------------------------------------------------------
+
+METHOD
+String::compare (const String& other) const -> int
+{
+	return m_string.compare (other.std_string());
+}
+
+// -------------------------------------------------------------------------------------------------
+
+METHOD
+String::trim (int n) -> void
+{
+	if (n > 0)
+		m_string = mid (0, length() - n).std_string();
+	else
+		m_string = mid (n, -1).std_string();
+}
+
+// -------------------------------------------------------------------------------------------------
+
+METHOD
+String::strip (const List<char>& unwanted) -> String
+{
+	String copy (m_string);
+
+	for (char c : unwanted)
+	{
+		for (int pos = 0; (pos = copy.find (String (c))) != -1;)
+			copy.remove (pos--);
+	}
+
+	return copy;
+}
+
+// -------------------------------------------------------------------------------------------------
+
+METHOD
+String::to_uppercase() const -> String
+{
+	String newstr (m_string);
+
+	for (char& c : newstr)
+	{
+		if (c >= 'a' and c <= 'z')
+			c -= 'a' - 'A';
+	}
+
+	return newstr;
+}
+
+// -------------------------------------------------------------------------------------------------
+
+METHOD
+String::to_lowercase() const -> String
+{
+	String newstr (m_string);
+
+	for (char& c : newstr)
+	{
+		if (c >= 'A' and c <= 'Z')
+			c += 'a' - 'A';
+	}
+
+	return newstr;
+}
+
+// -------------------------------------------------------------------------------------------------
+
+METHOD
+String::split (char del) const -> StringList
+{
+	String delimstr;
+	delimstr += del;
+	return split (delimstr);
+}
+
+// -------------------------------------------------------------------------------------------------
+
+METHOD
+String::split (const String& del) const -> StringList
+{
+	StringList res;
+	int a = 0;
+	int b;
+
+	// Find all separators and store the text left to them.
+	while ((b = find (del, a)) != -1)
+	{
+		String sub = mid (a, b);
+
+		if (sub.length() > 0)
+			res << sub;
+
+		a = b + del.length();
+	}
+
+	// Add the string at the right of the last separator
+	if (a < (int) length())
+		res.append (mid (a, length()));
+
+	return res;
+}
+
+// -------------------------------------------------------------------------------------------------
+
+METHOD
+String::replace (const char* a, const char* b) -> void
+{
+	long pos;
+
+	while ((pos = find (a)) != -1)
+		m_string = m_string.replace (pos, strlen (a), b);
+}
+
+// -------------------------------------------------------------------------------------------------
+
+METHOD
+String::count (char needle) const -> int
+{
+	int needles = 0;
+
+	for (const char & c : m_string)
+		if (c == needle)
+			needles++;
+
+	return needles;
+}
+
+// -------------------------------------------------------------------------------------------------
+
+METHOD
+String::mid (long a, long b) const -> String
+{
+	if (b == -1 or b > length())
+		b = length();
+
+	if (b == a)
+		return "";
+
+	if (b < a)
+		swap (a, b);
+
+	if (a == 0 and b == length())
+		return *this;
+
+	char* newstr = new char[b - a + 1];
+	strncpy (newstr, chars() + a, b - a);
+	newstr[b - a] = '\0';
+	String other (newstr);
+	delete[] newstr;
+	return other;
+}
+
+// -------------------------------------------------------------------------------------------------
+
+METHOD
+String::word_position (int n) const -> int
+{
+	int count = 0;
+
+	for (int i = 0; i < length(); ++i)
+	{
+		if (m_string[i] != ' ')
+			continue;
+
+		if (++count < n)
+			continue;
+
+		return i;
+	}
+
+	return -1;
+}
+
+// -------------------------------------------------------------------------------------------------
+
+METHOD
+String::find (const char* c, int a) const -> int
+{
+	int pos = m_string.find (c, a);
+
+	if (pos == int (std::string::npos))
+		return -1;
+
+	return pos;
+}
+
+// -------------------------------------------------------------------------------------------------
+
+METHOD
+String::find_last (const char* c, int a) const -> int
+{
+	if (a == -1 or a >= length())
+		a = length() - 1;
+
+	for (; a > 0; a--)
+		if (m_string[a] == c[0] and strncmp (chars() + a, c, strlen (c)) == 0)
+			return a;
+
+	return -1;
+}
+
+// -------------------------------------------------------------------------------------------------
+
+METHOD
+String::to_int (bool* ok, int base) const -> long
+{
+	errno = 0;
+	char* endptr;
+	long i = strtol (chars(), &endptr, base);
+
+	if (ok)
+		*ok = (errno == 0 and *endptr == '\0');
+
+	return i;
+}
+
+// -------------------------------------------------------------------------------------------------
+
+METHOD
+String::to_float (bool* ok) const -> float
+{
+	errno = 0;
+	char* endptr;
+	float i = strtof (chars(), &endptr);
+
+	if (ok != nullptr)
+		*ok = (errno == 0 and *endptr == '\0');
+
+	return i;
+}
+
+// -------------------------------------------------------------------------------------------------
+
+METHOD
+String::to_double (bool* ok) const -> double
+{
+	errno = 0;
+	char* endptr;
+	double i = strtod (chars(), &endptr);
+
+	if (ok != nullptr)
+		*ok = (errno == 0 and *endptr == '\0');
+
+	return i;
+}
+
+// -------------------------------------------------------------------------------------------------
+
+METHOD
+String::operator+ (const String& data) const -> String
+{
+	String newString = *this;
+	newString.append (data);
+	return newString;
+}
+
+// -------------------------------------------------------------------------------------------------
+
+METHOD
+String::operator+ (const char* data) const -> String
+{
+	String newstr = *this;
+	newstr.append (data);
+	return newstr;
+}
+
+// -------------------------------------------------------------------------------------------------
+
+METHOD
+String::is_numeric() const -> bool
+{
+	bool gotDot = false;
+
+	for (const char & c : m_string)
+	{
+		// Allow leading hyphen for negatives
+		if (&c == &m_string[0] and c == '-')
+			continue;
+
+		// Check for decimal point
+		if (!gotDot and c == '.')
+		{
+			gotDot = true;
+			continue;
+		}
+
+		if (c >= '0' and c <= '9')
+			continue; // Digit
+
+		// If the above cases didn't catch this character, it was
+		// illegal and this is therefore not a number.
+		return false;
+	}
+
+	return true;
+}
+
+// -------------------------------------------------------------------------------------------------
+
+METHOD
+String::ends_with (const String& other) -> bool
+{
+	if (length() < other.length())
+		return false;
+
+	const int ofs = length() - other.length();
+	return strncmp (chars() + ofs, other.chars(), other.length()) == 0;
+}
+
+// -------------------------------------------------------------------------------------------------
+
+METHOD
+String::starts_with (const String& other) -> bool
+{
+	if (length() < other.length())
+		return false;
+
+	return strncmp (chars(), other.chars(), other.length()) == 0;
+}
+
+// -------------------------------------------------------------------------------------------------
+
+METHOD
+String::sprintf (const char* fmtstr, ...) -> void
+{
+	char* buf;
+	int bufsize = 256;
+	va_list va;
+	va_start (va, fmtstr);
+
+	do
+		buf = new char[bufsize];
+	while (vsnprintf (buf, bufsize, fmtstr, va) >= bufsize);
+
+	va_end (va);
+	m_string = buf;
+	delete[] buf;
+}
+
+// -------------------------------------------------------------------------------------------------
+
+METHOD
+StringList::join (const String& delim) -> String
+{
+	String result;
+
+	for (const String& it : container())
+	{
+		if (result.is_empty() == false)
+			result += delim;
+
+		result += it;
+	}
+
+	return result;
+}
+
+// -------------------------------------------------------------------------------------------------
+
+METHOD
+String::mask_against (const String& pattern) const -> bool
+{
+	// Elevate to uppercase for case-insensitive matching
+	String pattern_upper = pattern.to_uppercase();
+	String this_upper = to_uppercase();
+	const char* maskstring = pattern_upper.chars();
+	const char* mptr = &maskstring[0];
+
+	for (const char* sptr = this_upper.chars(); *sptr != '\0'; sptr++)
+	{
+		if (*mptr == '?')
+		{
+			if (*(sptr + 1) == '\0')
+			{
+				// ? demands that there's a character here and there wasn't.
+				// Therefore, mask matching fails
+				return false;
+			}
+		}
+		else if (*mptr == '*')
+		{
+			char end = *(++mptr);
+
+			// If '*' is the final character of the message, all of the remaining
+			// string matches against the '*'. We don't need to bother checking
+			// the string any further.
+			if (end == '\0')
+				return true;
+
+			// Skip to the end character
+			while (*sptr != end and *sptr != '\0')
+				sptr++;
+
+			// String ended while the mask still had stuff
+			if (*sptr == '\0')
+				return false;
+		}
+		else if (*sptr != *mptr)
+			return false;
+
+		mptr++;
+	}
+
+	return true;
+}
+
+// -------------------------------------------------------------------------------------------------
+
+METHOD
+String::from_number (int a) -> String
+{
+	char buf[32];
+	::sprintf (buf, "%d", a);
+	return String (buf);
+}
+
+// -------------------------------------------------------------------------------------------------
+
+METHOD
+String::from_number (long a) -> String
+{
+	char buf[32];
+	::sprintf (buf, "%ld", a);
+	return String (buf);
+}
+
+// -------------------------------------------------------------------------------------------------
+
+METHOD
+String::from_number (unsigned long a) -> String
+{
+	char buf[32];
+	::sprintf (buf, "%lu", a);
+	return String (buf);
+}
+
+// -------------------------------------------------------------------------------------------------
+
+METHOD
+String::from_number (double a) -> String
+{
+	char buf[64];
+	::sprintf (buf, "%f", a);
+	return String (buf);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/sources/mystring.h	Wed Dec 10 19:17:00 2014 +0200
@@ -0,0 +1,523 @@
+/*
+	Copyright 2014 Teemu Piippo
+	All rights reserved.
+
+	Redistribution and use in source and binary forms, with or without
+	modification, are permitted provided that the following conditions
+	are met:
+
+	1. Redistributions of source code must retain the above copyright
+	   notice, this list of conditions and the following disclaimer.
+	2. Redistributions in binary form must reproduce the above copyright
+	   notice, this list of conditions and the following disclaimer in the
+	   documentation and/or other materials provided with the distribution.
+	3. Neither the name of the copyright holder nor the names of its
+	   contributors may be used to endorse or promote products derived from
+	   this software without specific prior written permission.
+
+	THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+	"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+	TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+	PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER
+	OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+	EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+	PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+	PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+	LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+	NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+	SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#pragma once
+#include <deque>
+#include <string>
+#include <stdarg.h>
+#include "basics.h"
+#include "list.h"
+
+class String;
+class StringList;
+
+class String
+{
+public:
+	String() {}
+
+	explicit String (char a) :
+		m_string ({ a, '\0' }) {}
+
+	String (const char* data) :
+		m_string (data) {}
+
+	String (const std::string& data) :
+		m_string (data) {}
+
+	String (const Vector<char>& data) :
+		m_string (data.data(), data.size()) {}
+
+	inline METHOD append (const char* data) -> void;
+	inline METHOD append (char data) -> void;
+	inline METHOD append (const String& data) -> void;
+	inline METHOD begin() -> std::string::iterator;
+	inline METHOD begin() const -> std::string::const_iterator;
+	inline METHOD clear() -> void;
+	       METHOD compare (const String& other) const -> int;
+	       METHOD count (char needle) const -> int;
+	inline METHOD chars() const -> const char*;
+	inline METHOD end() -> std::string::iterator;
+	inline METHOD end() const -> std::string::const_iterator;
+	       METHOD ends_with (const String& other) -> bool;
+	       METHOD find (const char* c, int a = 0) const -> int;
+	       METHOD to_lowercase() const -> String;
+	inline METHOD index_difference (int a, int b) -> int;
+	inline METHOD insert (int pos, char c) -> void;
+	inline METHOD is_empty() const -> bool;
+	       METHOD is_numeric() const -> bool;
+	       METHOD find_last (const char* c, int a = -1) const -> int;
+	inline METHOD length() const -> int;
+	       METHOD mask_against (const String& pattern) const -> bool;
+	       METHOD mid (long a, long b = -1) const -> String;
+	inline METHOD modify_index (int& a) -> void;
+	inline METHOD prepend (String a) -> void;
+	inline METHOD remove (int pos) -> void;
+	inline METHOD remove (int pos, int len) -> void;
+	inline METHOD remove_from_end (int len) -> void;
+	inline METHOD remove_from_start (int len) -> void;
+	       METHOD replace (const char* a, const char* b) -> void;
+	inline METHOD replace (int pos, int n, const String& a) -> void;
+	inline METHOD shrink_to_fit() -> void;
+	       METHOD split (const String& del) const -> StringList;
+	       METHOD split (char del) const -> StringList;
+	       METHOD sprintf (const char* fmtstr, ...) -> void;
+	       METHOD starts_with (const String& other) -> bool;
+	inline METHOD std_string() const -> const std::string&;
+	inline METHOD strip (char unwanted) -> String;
+	       METHOD strip (const List<char>& unwanted) -> String;
+	       METHOD to_double (bool* ok = nullptr) const -> double;
+	       METHOD to_float (bool* ok = nullptr) const -> float;
+	       METHOD to_int (bool* ok = nullptr, int base = 10) const -> long;
+	       METHOD trim (int n) -> void;
+	       METHOD to_uppercase() const -> String;
+	       METHOD word_position (int n) const -> int;
+
+	static METHOD from_number (int a) -> String;
+	static METHOD from_number (long a) -> String;
+	static METHOD from_number (unsigned long a) -> String;
+	static METHOD from_number (double a) -> String;
+
+	       METHOD operator+ (const String& data) const -> String;
+	       METHOD operator+ (const char* data) const -> String;
+	inline METHOD operator+ (int num) const -> String;
+	inline METHOD operator+= (const String& data) -> String&;
+	inline METHOD operator+= (const char* data) -> String&;
+	inline METHOD operator+= (int num) -> String&;
+	inline METHOD operator+= (char data) -> String&;
+	inline METHOD operator== (const String& other) const -> bool;
+	inline METHOD operator== (const char* other) const -> bool;
+	inline METHOD operator!= (const String& other) const -> bool;
+	inline METHOD operator!= (const char* other) const -> bool;
+	inline METHOD operator> (const String& other) const -> bool;
+	inline METHOD operator< (const String& other) const -> bool;
+	inline operator const char*() const;
+	inline operator const std::string&() const;
+
+private:
+	std::string m_string;
+};
+
+class StringList : public List<String>
+{
+public:
+	template<typename... Args>
+	StringList (Args... args) :
+		List<String> (args...) {}
+
+	METHOD join (const String& delim) -> String;
+};
+
+inline FUNCTION operator== (const char* a, const String& b) -> bool;
+inline FUNCTION operator+ (const char* a, const String& b) -> String;
+
+//
+// -------------------------------------------------------------------------------------------------
+//
+// IMPLEMENTATIONS
+//
+
+inline METHOD
+String::is_empty() const -> bool
+{
+	return m_string[0] == '\0';
+}
+
+//
+// -------------------------------------------------------------------------------------------------
+//
+
+inline METHOD
+String::append (const char* data) -> void
+{
+	m_string.append (data);
+}
+
+//
+// -------------------------------------------------------------------------------------------------
+//
+
+inline METHOD
+String::append (char data) -> void
+{
+	m_string.push_back (data);
+}
+
+//
+// -------------------------------------------------------------------------------------------------
+//
+
+inline METHOD
+String::append (const String& data) -> void
+{
+	m_string.append (data.chars());
+}
+
+//
+// -------------------------------------------------------------------------------------------------
+//
+
+inline METHOD
+String::begin() -> std::string::iterator
+{
+	return m_string.begin();
+}
+
+//
+// -------------------------------------------------------------------------------------------------
+//
+
+inline METHOD
+String::begin() const -> std::string::const_iterator
+{
+	return m_string.cbegin();
+}
+
+//
+// -------------------------------------------------------------------------------------------------
+//
+
+inline const char* String::chars() const
+{
+	return m_string.c_str();
+}
+
+//
+// -------------------------------------------------------------------------------------------------
+//
+
+inline METHOD
+String::end() -> std::string::iterator
+{
+	return m_string.end();
+}
+
+//
+// -------------------------------------------------------------------------------------------------
+//
+
+inline METHOD
+String::end() const -> std::string::const_iterator
+{
+	return m_string.end();
+}
+
+//
+// -------------------------------------------------------------------------------------------------
+//
+
+inline METHOD
+String::clear() -> void
+{
+	m_string.clear();
+}
+
+//
+// -------------------------------------------------------------------------------------------------
+//
+
+inline METHOD
+String::remove (int pos) -> void
+{
+	m_string.erase (m_string.begin() + pos);
+}
+
+//
+// -------------------------------------------------------------------------------------------------
+//
+
+inline METHOD
+String::insert (int pos, char c) -> void
+{
+	m_string.insert (m_string.begin() + pos, c);
+}
+
+//
+// -------------------------------------------------------------------------------------------------
+//
+
+inline METHOD
+String::length() const -> int
+{
+	return m_string.length();
+}
+
+//
+// -------------------------------------------------------------------------------------------------
+//
+
+inline METHOD
+String::remove (int pos, int len) -> void
+{
+	m_string.replace (pos, len, "");
+}
+
+//
+// -------------------------------------------------------------------------------------------------
+//
+
+inline METHOD
+String::remove_from_start (int len) -> void
+{
+	remove (0, len);
+}
+
+//
+// -------------------------------------------------------------------------------------------------
+//
+
+inline METHOD
+String::remove_from_end (int len) -> void
+{
+	remove (length() - len, len);
+}
+
+//
+// -------------------------------------------------------------------------------------------------
+//
+
+inline METHOD
+String::replace (int pos, int n, const String& a) -> void
+{
+	m_string.replace (pos, n, a.chars());
+}
+
+//
+// -------------------------------------------------------------------------------------------------
+//
+
+inline METHOD
+String::shrink_to_fit() -> void
+{
+	m_string.shrink_to_fit();
+}
+
+//
+// -------------------------------------------------------------------------------------------------
+//
+
+inline const std::string& String::std_string() const
+{
+	return m_string;
+}
+
+//
+// -------------------------------------------------------------------------------------------------
+//
+
+inline METHOD
+String::strip (char unwanted) -> String
+{
+	return strip ({unwanted});
+}
+
+//
+// -------------------------------------------------------------------------------------------------
+//
+
+inline METHOD
+String::operator+ (int num) const -> String
+{
+	return *this + String::from_number (num);
+}
+
+//
+// -------------------------------------------------------------------------------------------------
+//
+
+inline METHOD
+String::operator+= (const String& data) -> String&
+{
+	append (data);
+	return *this;
+}
+
+//
+// -------------------------------------------------------------------------------------------------
+//
+
+inline METHOD
+String::operator+= (const char* data) -> String&
+{
+	append (data);
+	return *this;
+}
+
+//
+// -------------------------------------------------------------------------------------------------
+//
+
+inline METHOD
+String::operator+= (int num) -> String&
+{
+	return operator+= (String::from_number (num));
+}
+
+//
+// -------------------------------------------------------------------------------------------------
+//
+
+inline METHOD
+String::prepend (String a) -> void
+{
+	m_string = (a + m_string).std_string();
+}
+
+//
+// -------------------------------------------------------------------------------------------------
+//
+
+inline METHOD
+String::operator+= (char data) -> String&
+{
+	append (data);
+	return *this;
+}
+
+//
+// -------------------------------------------------------------------------------------------------
+//
+
+inline METHOD
+String::operator== (const String& other) const -> bool
+{
+	return std_string() == other.std_string();
+}
+
+//
+// -------------------------------------------------------------------------------------------------
+//
+
+inline METHOD
+String::operator== (const char* other) const -> bool
+{
+	return operator== (String (other));
+}
+
+//
+// -------------------------------------------------------------------------------------------------
+//
+
+inline METHOD
+String::operator!= (const String& other) const -> bool
+{
+	return std_string() != other.std_string();
+}
+
+//
+// -------------------------------------------------------------------------------------------------
+//
+
+inline METHOD
+String::operator!= (const char* other) const -> bool
+{
+	return operator!= (String (other));
+}
+
+//
+// -------------------------------------------------------------------------------------------------
+//
+
+inline METHOD
+String::operator> (const String& other) const -> bool
+{
+	return std_string() > other.std_string();
+}
+
+//
+// -------------------------------------------------------------------------------------------------
+//
+
+inline METHOD
+String::operator< (const String& other) const -> bool
+{
+	return std_string() < other.std_string();
+}
+
+//
+// -------------------------------------------------------------------------------------------------
+//
+
+inline String::operator const char*() const
+{
+	return chars();
+}
+
+//
+// -------------------------------------------------------------------------------------------------
+//
+
+inline String::operator const std::string&() const
+{
+	return std_string();
+}
+
+//
+// -------------------------------------------------------------------------------------------------
+//
+
+inline METHOD
+String::modify_index (int& a) -> void
+{
+	if (a < 0)
+		a = length() - a;
+}
+
+//
+// -------------------------------------------------------------------------------------------------
+//
+
+inline METHOD
+String::index_difference (int a, int b) -> int
+{
+	modify_index (a);
+	modify_index (b);
+	return b - a;
+}
+
+//
+// -------------------------------------------------------------------------------------------------
+//
+
+inline FUNCTION
+operator== (const char* a, const String& b) -> bool
+{
+	return b == a;
+}
+
+//
+// -------------------------------------------------------------------------------------------------
+//
+
+inline FUNCTION
+operator+ (const char* a, const String& b) -> String
+{
+	return String (a) + b;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/sources/range.h	Wed Dec 10 19:17:00 2014 +0200
@@ -0,0 +1,240 @@
+/*
+	Copyright 2014 Teemu Piippo
+	All rights reserved.
+
+	Redistribution and use in source and binary forms, with or without
+	modification, are permitted provided that the following conditions
+	are met:
+
+	1. Redistributions of source code must retain the above copyright
+	   notice, this list of conditions and the following disclaimer.
+	2. Redistributions in binary form must reproduce the above copyright
+	   notice, this list of conditions and the following disclaimer in the
+	   documentation and/or other materials provided with the distribution.
+	3. Neither the name of the copyright holder nor the names of its
+	   contributors may be used to endorse or promote products derived from
+	   this software without specific prior written permission.
+
+	THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+	"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+	TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+	PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER
+	OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+	EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+	PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+	PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+	LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+	NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+	SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#pragma once
+#include <algorithm>
+#include <memory>
+
+//
+// -------------------------------------------------------------------------------------------------
+//
+
+template<typename T>
+class Range
+{
+	T m_a;
+	T m_b;
+	T m_step;
+
+public:
+	struct Iterator
+	{
+		T value;
+		T step;
+		inline METHOD operator*() -> T&;
+		inline METHOD operator== (const Iterator& other) const -> bool;
+		inline METHOD operator!= (const Iterator& other) const -> bool;
+		inline METHOD operator++() -> Iterator&;
+	};
+
+	Range (const T& a, const T& b, const T& step = 1);
+	Range();
+
+	METHOD begin() const -> Iterator;
+	METHOD end() const -> Iterator;
+	METHOD min() const -> T;
+	METHOD max() const -> T;
+	METHOD check_bounds() -> void;
+	METHOD contains (const T& c) const -> bool;
+	METHOD contains_exclusive (const T& c) const -> bool;
+	METHOD overlaps (Range<T> const& other) const -> bool;
+	METHOD operator== (Range<T> const& other) const -> bool;
+	METHOD operator!= (Range<T> const& other) const -> bool;
+};
+
+//
+// -------------------------------------------------------------------------------------------------
+//
+
+template<typename T> inline METHOD
+Range<T>::Iterator::operator*() -> T&
+{
+	return value;
+}
+
+//
+// -------------------------------------------------------------------------------------------------
+//
+
+template<typename T> inline METHOD
+Range<T>::Iterator::operator== (const Iterator& other) const -> bool
+{
+	return value == other.value;
+}
+
+//
+// -------------------------------------------------------------------------------------------------
+//
+
+template<typename T> inline METHOD
+Range<T>::Iterator::operator!= (const Iterator& other) const -> bool
+{
+	return value != other.value;
+}
+
+//
+// -------------------------------------------------------------------------------------------------
+//
+
+template<typename T> inline METHOD
+Range<T>::Iterator::operator++() -> Iterator&
+{
+	value += step;
+	return *this;
+}
+
+//
+// -------------------------------------------------------------------------------------------------
+//
+
+template<typename T>
+Range<T>::Range (const T& a, const T& b, const T& step) :
+	m_a (a),
+	m_b (b),
+	m_step (step)
+{
+	check_bounds();
+}
+
+//
+// -------------------------------------------------------------------------------------------------
+//
+
+template<typename T>
+Range<T>::Range() :
+	m_a (T()),
+	m_b (T()) {}
+
+//
+// -------------------------------------------------------------------------------------------------
+//
+
+template<typename T> METHOD
+Range<T>::check_bounds() -> void
+{
+	if (m_b < m_a)
+		std::swap (m_a, m_b);
+}
+
+//
+// -------------------------------------------------------------------------------------------------
+//
+
+template<typename T> METHOD
+Range<T>::contains (const T& c) const -> bool
+{
+	return (c >= m_a) and (c <= m_b);
+}
+
+//
+// -------------------------------------------------------------------------------------------------
+//
+
+template<typename T> METHOD
+Range<T>::contains_exclusive (const T& c) const -> bool
+{
+	return (c > m_a) and (c < m_b);
+}
+
+//
+// -------------------------------------------------------------------------------------------------
+//
+
+template<typename T> METHOD
+Range<T>::overlaps (Range<T> const& other) const -> bool
+{
+	return contains (other.m_a) or contains (other.m_b);
+}
+
+//
+// -------------------------------------------------------------------------------------------------
+//
+
+template<typename T> METHOD
+Range<T>::operator== (Range<T> const& other) const -> bool
+{
+	return m_a == other.m_a and m_b == other.m_b;
+}
+
+//
+// -------------------------------------------------------------------------------------------------
+//
+
+template<typename T> METHOD
+Range<T>::operator!= (Range<T> const& other) const -> bool
+{
+	return not operator== (other);
+}
+
+//
+// -------------------------------------------------------------------------------------------------
+//
+
+template<typename T> METHOD
+Range<T>::min() const -> T
+{
+	return m_a;
+}
+
+//
+// -------------------------------------------------------------------------------------------------
+//
+
+template<typename T> METHOD
+Range<T>::max() const -> T
+{
+	return m_b;
+}
+
+//
+// -------------------------------------------------------------------------------------------------
+//
+
+template<typename T> METHOD
+Range<T>::begin() const -> Iterator
+{
+	Iterator it;
+	it.value = min();
+	it.step = m_step;
+	return it;
+}
+
+//
+// -------------------------------------------------------------------------------------------------
+//
+
+template<typename T> METHOD
+Range<T>::end() const -> Iterator
+{
+	Iterator it;
+	it.value = max() + 1;
+	it.step = m_step;
+	return it;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/sources/version.cpp	Wed Dec 10 19:17:00 2014 +0200
@@ -0,0 +1,56 @@
+/*
+	Copyright 2014 Teemu Piippo
+	All rights reserved.
+
+	Redistribution and use in source and binary forms, with or without
+	modification, are permitted provided that the following conditions
+	are met:
+
+	1. Redistributions of source code must retain the above copyright
+	   notice, this list of conditions and the following disclaimer.
+	2. Redistributions in binary form must reproduce the above copyright
+	   notice, this list of conditions and the following disclaimer in the
+	   documentation and/or other materials provided with the distribution.
+	3. Neither the name of the copyright holder nor the names of its
+	   contributors may be used to endorse or promote products derived from
+	   this software without specific prior written permission.
+
+	THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+	"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+	TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+	PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER
+	OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+	EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+	PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+	PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+	LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+	NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+	SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+//
+// This file uses C stuff only in order to compile quick, as this file
+// is recompiled whenever hginfo.h changes!
+//
+
+#include <stdio.h>
+#include <string.h>
+#include "version.h"
+#include "hginfo.h"
+
+FUNCTION
+full_version_string() -> const char*
+{
+	static char buffer[128] = {0};
+
+	if (buffer[0] == '\0')
+		snprintf (buffer, sizeof buffer, "%s-%s", version_string(), REVISION_STRING);
+
+	return buffer;
+}
+
+FUNCTION
+changeset_date_string() -> const char*
+{
+	return REVISION_DISPLAY_DATE;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/sources/version.h	Wed Dec 10 19:17:00 2014 +0200
@@ -0,0 +1,70 @@
+/*
+	Copyright 2014 Teemu Piippo
+	All rights reserved.
+
+	Redistribution and use in source and binary forms, with or without
+	modification, are permitted provided that the following conditions
+	are met:
+
+	1. Redistributions of source code must retain the above copyright
+	   notice, this list of conditions and the following disclaimer.
+	2. Redistributions in binary form must reproduce the above copyright
+	   notice, this list of conditions and the following disclaimer in the
+	   documentation and/or other materials provided with the distribution.
+	3. Neither the name of the copyright holder nor the names of its
+	   contributors may be used to endorse or promote products derived from
+	   this software without specific prior written permission.
+
+	THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+	"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+	TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+	PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER
+	OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+	EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+	PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+	PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+	LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+	NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+	SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#pragma once
+#include "basics.h"
+
+#define APPNAME "hurgharama"
+#define VERSION_MAJOR 1
+#define VERSION_MINOR 0
+#define VERSION_PATCH 0
+
+//
+// Version string
+//
+#if VERSION_PATCH == 0
+# define VERSION_STRING \
+	MACRO_TO_STRING (VERSION_MAJOR) \
+	"." MACRO_TO_STRING (VERSION_MINOR)
+#else
+# define VERSION_STRING \
+	MACRO_TO_STRING (VERSION_MAJOR) \
+	"." MACRO_TO_STRING (VERSION_MINOR) \
+	"." MACRO_TO_STRING (VERSION_PATCH)
+#endif
+
+//
+// Returns the bare version string (1.2.3)
+//
+inline FUNCTION
+version_string() -> const char*
+{
+	return VERSION_STRING;
+}
+
+//
+// Returns full version string, with hash (1.2.3-abcd123)
+//
+FUNCTION full_version_string() -> const char*;
+
+//
+// Returns changeset date string
+//
+FUNCTION changeset_date_string() -> const char*;
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/updaterevision/CMakeLists.txt	Wed Dec 10 19:17:00 2014 +0200
@@ -0,0 +1,17 @@
+project (hurgharama)
+add_subdirectory (updaterevision)
+
+set
+(
+	SOURCE_FILES
+	sources/main.cpp
+	sources/version.cpp
+)
+
+set (CURSES_NEED_NCURSES, True)
+set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++0x -W -Wall")
+find_package (Curses)
+include_directories (${CURSES_INCUDE_DIRS})
+include_directories (${CMAKE_CURRENT_BINARY_DIR})
+add_executable (${PROJECT_NAME} ${SOURCE_FILES})
+target_link_libraries (${PROJECT_NAME} ${CMAKE_LIBRARIES})
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/updaterevision/updaterevision.py	Wed Dec 10 19:17:00 2014 +0200
@@ -0,0 +1,75 @@
+#
+#	Copyright 2014 Teemu Piippo
+#	All rights reserved.
+#
+#	Redistribution and use in source and binary forms, with or without
+#	modification, are permitted provided that the following conditions
+#	are met:
+#
+#	1. Redistributions of source code must retain the above copyright
+#	   notice, this list of conditions and the following disclaimer.
+#	2. Redistributions in binary form must reproduce the above copyright
+#	   notice, this list of conditions and the following disclaimer in the
+#	   documentation and/or other materials provided with the distribution.
+#	3. Neither the name of the copyright holder nor the names of its
+#	   contributors may be used to endorse or promote products derived from
+#	   this software without specific prior written permission.
+#
+#	THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+#	"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+#	TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+#	PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER
+#	OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+#	EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+#	PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+#	PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+#	LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+#	NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+#	SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+#
+
+import sys
+import subprocess
+from datetime import datetime
+
+if len (sys.argv) != 2:
+    print 'usage: %s <output>' % sys.argv[0]
+    quit (1)
+
+oldrev = ''
+
+try:
+    with open (sys.argv[1]) as fp:
+        oldrev = fp.readline().replace ('\n', '').replace ('// ', '')
+except IOError:
+    pass
+
+data = subprocess.check_output (['hg', 'log', '-r.', '--template',
+    '{node|short} {branch} {date|hgdate}']).replace ('\n', '').split (' ')
+
+rev = data[0]
+branch = data[1]
+timestamp = int (data[2])
+date = datetime.utcfromtimestamp (timestamp)
+datestring = date.strftime ('%y%m%d-%H%M') if date.year >= 2000 else '000000-0000'
+
+if len(rev) > 7:
+    rev = rev[0:7]
+
+if subprocess.check_output (['hg', 'id', '-n']).replace ('\n', '')[-1] == '+':
+    rev += '+'
+
+if branch != 'default':
+    rev += '-%s' % branch
+
+if rev == oldrev:
+    print "%s is up to date at %s" % (sys.argv[1], rev)
+    quit (0)
+
+with open (sys.argv[1], 'w') as fp:
+    fp.write ('// %s\n' % rev)
+    fp.write ('#define REVISION_STRING "%s"\n' % rev)
+    fp.write ('#define REVISION_DATE "%s"\n' % datestring)
+    fp.write ('#define REVISION_DISPLAY_DATE "%s"\n' % date.strftime ('%d %b %Y'))
+    fp.write ('#define REVISION_TIME %d\n' % int (timestamp))
+    print '%s updated to %s' % (sys.argv[1], rev)

mercurial