/* iostring.hh - Read and write strings in parts.
 * Copyright 2007-2008 Bas Wijnen <wijnen@debian.org>
 *
 * This program 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.
 *
 * This program 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 this program.  If not, see <http://www.gnu.org/licenses/>.
 */

#ifndef SHEVEK_IOSTRING_HH
#define SHEVEK_IOSTRING_HH

#include <string>
#include <stack>
#include <sstream>
#include <glibmm.h>

namespace shevek
{
	/// shevek::istring is a C++ version of scanf.
	/** It uses templates to allow expanding it to user-defined types,
	 *  but it still uses a format string to make it better translatable.
	 */
	class istring
	{
		// The data to parse.
		Glib::ustring data;
		// Pointer to data still to be parsed.
		std::stack <Glib::ustring::size_type> pos;

		// Read a variable.
		template <typename T>
		bool read_var (Glib::ustring const &format, T &ret, Glib::ustring::size_type &inpos);

		// Read away whitespace.
		void del_whitespace ();

		// Check for a constant.
		bool read_const (Glib::ustring const &format, Glib::ustring::size_type &inpos);
	public:
		// Constructors: fill data and make one stack level.
		/// Create a new istring with no data.
		istring () { pos.push (0); }
		/// Create a new istring with data.
		istring (Glib::ustring const &str) { init (str); }
		/// Set new data to an existing istring.
		void init (Glib::ustring const &str)
		{ data = str; while (!pos.empty ()) pos.pop (); pos.push (0); }

		// Stack functions.
		/// Push the current position to the stack so it can be restored later.
		void push () { pos.push (pos.top ()); }
		/// Pop the last pushed position from the stack.
		/** If keep is true or not given, the current position is restored to the last position.
		 *  If it is false, the current position is not changed.
		 */
		int pop (bool keep = false);
		/// Set the current position to 0, but don't change the stack.
		void reset () { pos.top () = 0; }

		/// Get remaining string.
		Glib::ustring rest () const { return data.substr (pos.top ()); }

		/// Skip some characters.
		void skip (Glib::ustring::size_type p) { pos.top () += p; }

		/// Read a constant string from the input.
		bool operator() (Glib::ustring const &format)
		{
			push ();
			Glib::ustring::size_type inpos = 0;
			bool ret = read_const (format, inpos)
				&& inpos == format.size ();
			pop (ret);
			return ret;
		}

		/// Read a string containing one argument from the input.
		template <typename T1>
		bool operator() (Glib::ustring const &format, T1 &arg1)
		{
			push ();
			Glib::ustring::size_type inpos = 0;
			bool ret = read_const (format, inpos)
				&& read_var (format, arg1, inpos)
				&& read_const (format, inpos)
				&& inpos == format.size ();
			pop (ret);
			return ret;
		}

		/// Read a string containing two arguments from the input.
		template <typename T1, typename T2>
		bool operator() (Glib::ustring const &format, T1 &arg1,
				T2 &arg2)
		{
			push ();
			Glib::ustring::size_type inpos = 0;
			bool ret = read_const (format, inpos)
				&& read_var (format, arg1, inpos)
				&& read_const (format, inpos)
				&& read_var (format, arg2, inpos)
				&& read_const (format, inpos)
				&& inpos == format.size ();
			pop (ret);
			return ret;
		}

		/// Read a string containing three arguments from the input.
		template <typename T1, typename T2, typename T3>
		bool operator() (Glib::ustring const &format, T1 &arg1,
				T2 &arg2, T3 &arg3)
		{
			push ();
			Glib::ustring::size_type inpos = 0;
			bool ret = read_const (format, inpos)
				&& read_var (format, arg1, inpos)
				&& read_const (format, inpos)
				&& read_var (format, arg2, inpos)
				&& read_const (format, inpos)
				&& read_var (format, arg3, inpos)
				&& read_const (format, inpos)
				&& inpos == format.size ();
			pop (ret);
			return ret;
		}

		/// Read a string containing four arguments from the input.
		template <typename T1, typename T2, typename T3, typename T4>
		bool operator() (Glib::ustring const &format, T1 &arg1,
				T2 &arg2, T3 &arg3, T4 &arg4)
		{
			push ();
			Glib::ustring::size_type inpos = 0;
			bool ret = read_const (format, inpos)
				&& read_var (format, arg1, inpos)
				&& read_const (format, inpos)
				&& read_var (format, arg2, inpos)
				&& read_const (format, inpos)
				&& read_var (format, arg3, inpos)
				&& read_const (format, inpos)
				&& read_var (format, arg4, inpos)
				&& read_const (format, inpos)
				&& inpos == format.size ();
			pop (ret);
			return ret;
		}

		/// Read a string containing five arguments from the input.
		template <typename T1, typename T2, typename T3, typename T4,
			 typename T5>
		bool operator() (Glib::ustring const &format, T1 &arg1,
				T2 &arg2, T3 &arg3, T4 &arg4, T5 &arg5)
		{
			push ();
			Glib::ustring::size_type inpos = 0;
			bool ret = read_const (format, inpos)
				&& read_var (format, arg1, inpos)
				&& read_const (format, inpos)
				&& read_var (format, arg2, inpos)
				&& read_const (format, inpos)
				&& read_var (format, arg3, inpos)
				&& read_const (format, inpos)
				&& read_var (format, arg4, inpos)
				&& read_const (format, inpos)
				&& read_var (format, arg5, inpos)
				&& read_const (format, inpos)
				&& inpos == format.size ();
			pop (ret);
			return ret;
		}

		/// Read a string containing six arguments from the input.
		template <typename T1, typename T2, typename T3, typename T4,
			 typename T5, typename T6>
		bool operator() (Glib::ustring const &format, T1 &arg1,
				T2 &arg2, T3 &arg3, T4 &arg4, T5 &arg5,
				T6 &arg6)
		{
			push ();
			Glib::ustring::size_type inpos = 0;
			bool ret = read_const (format, inpos)
				&& read_var (format, arg1, inpos)
				&& read_const (format, inpos)
				&& read_var (format, arg2, inpos)
				&& read_const (format, inpos)
				&& read_var (format, arg3, inpos)
				&& read_const (format, inpos)
				&& read_var (format, arg4, inpos)
				&& read_const (format, inpos)
				&& read_var (format, arg5, inpos)
				&& read_const (format, inpos)
				&& read_var (format, arg6, inpos)
				&& read_const (format, inpos)
				&& inpos == format.size ();
			pop (ret);
			return ret;
		}

		/// Read a string containing seven arguments from the input.
		template <typename T1, typename T2, typename T3, typename T4,
			 typename T5, typename T6, typename T7>
		bool operator() (Glib::ustring const &format, T1 &arg1,
				T2 &arg2, T3 &arg3, T4 &arg4, T5 &arg5,
				T6 &arg6, T7 &arg7)
		{
			push ();
			Glib::ustring::size_type inpos = 0;
			bool ret = read_const (format, inpos)
				&& read_var (format, arg1, inpos)
				&& read_const (format, inpos)
				&& read_var (format, arg2, inpos)
				&& read_const (format, inpos)
				&& read_var (format, arg3, inpos)
				&& read_const (format, inpos)
				&& read_var (format, arg4, inpos)
				&& read_const (format, inpos)
				&& read_var (format, arg5, inpos)
				&& read_const (format, inpos)
				&& read_var (format, arg6, inpos)
				&& read_const (format, inpos)
				&& read_var (format, arg7, inpos)
				&& read_const (format, inpos)
				&& inpos == format.size ();
			pop (ret);
			return ret;
		}

		/// Read a string containing eight arguments from the input.
		template <typename T1, typename T2, typename T3, typename T4,
			 typename T5, typename T6, typename T7, typename T8>
		bool operator() (Glib::ustring const &format, T1 &arg1,
				T2 &arg2, T3 &arg3, T4 &arg4, T5 &arg5,
				T6 &arg6, T7 &arg7, T8 &arg8)
		{
			push ();
			Glib::ustring::size_type inpos = 0;
			bool ret = read_const (format, inpos)
				&& read_var (format, arg1, inpos)
				&& read_const (format, inpos)
				&& read_var (format, arg2, inpos)
				&& read_const (format, inpos)
				&& read_var (format, arg3, inpos)
				&& read_const (format, inpos)
				&& read_var (format, arg4, inpos)
				&& read_const (format, inpos)
				&& read_var (format, arg5, inpos)
				&& read_const (format, inpos)
				&& read_var (format, arg6, inpos)
				&& read_const (format, inpos)
				&& read_var (format, arg7, inpos)
				&& read_const (format, inpos)
				&& read_var (format, arg8, inpos)
				&& read_const (format, inpos)
				&& inpos == format.size ();
			pop (ret);
			return ret;
		}

		/// Read a variable from given data and return it.
		/** This allows using a shevek::istring in an expression without the need to create a new variable for it.
		 *  If the input doesn't match the format, def is returned.
		 */
		template <typename T>
		static T direct (Glib::ustring const &data,
				Glib::ustring const &format,
				T def = T ())
		{
			T ret;
			istring tmp (data);
			if (!tmp (format, ret))
				ret = def;
			return ret;
		}
	};

	/// Template function for reading any type.
	/** The default implementation uses a std::istringstream extractor.
	 *  This function can be specialized by a program to allow reading custom types with a shevek::istring.
	 */
	template <typename T>
	bool istring::read_var (Glib::ustring const &format, T &ret,
			Glib::ustring::size_type &inpos)
	{
		++inpos;
		std::istringstream s (data.substr (pos.top ()));
		s >> ret;
		std::streampos p = s.tellg ();
		if (!s || p < 0)
			return false;
		pos.top () += p;
		return true;
	}

	/// Specialization of read_var for double.
	template <> bool istring::read_var <double> (Glib::ustring const &format, double &ret, Glib::ustring::size_type &inpos);

	/// Specialization of read_var for float.
	template <> bool istring::read_var <float> (Glib::ustring const &format, float &ret, Glib::ustring::size_type &inpos);

	/// Specialization of read_var for Glib::ustring.
	template <> bool istring::read_var <Glib::ustring> (Glib::ustring const &format, Glib::ustring &ret, Glib::ustring::size_type &inpos);

	/// Specialization of read_var for int.
	template <> bool istring::read_var <int> (Glib::ustring const &format, int &ret, Glib::ustring::size_type &inpos);

	/// Specialization of read_var for unsigned.
	template <> bool istring::read_var <unsigned> (Glib::ustring const &format, unsigned &ret, Glib::ustring::size_type &inpos);

	/// shevek::ostring is a C++ version of printf.
	/** It uses templates to allow expanding it to user-defined types, but it still uses a format string to make it better translatable.
	 *  ostring works on utf-8 strings (Glib::ustring).  For std::string, use rostring.
	 */
	class ostring
	{
		Glib::ustring data;
		Glib::ustring format;
		Glib::ustring::size_type pos;
		void write_const ();
		template <typename T> void write_var (T const &a,
				Glib::ustring const &flags,
				unsigned width, unsigned precision);
		template <typename T> void write_var_raw (T const &a);
		ostring &init (Glib::ustring const &f);
		// If you get a linker error to this line, you have tried to insert a std::string into a shevek::ostring.
		// Fix the program by turning it into a Glib::ustring first, or by using a shevek::rostring instead.
		template <typename T> ostring &operator ()(T const &a) { write_const (); write_var_raw (a); return *this; }
		ostring &operator ()();
	public:
		/// Use the result as a string.
		operator Glib::ustring () const { return data; }
		/// Use the result as a string.
		Glib::ustring operator+ (Glib::ustring const &that) const { return data + that; }
		/// Send the result to an ostream.
		friend std::ostream &operator<< (std::ostream &s, ostring const &o) { return s << o.data; }
		/// Create a string with a constant format.
		ostring (Glib::ustring const &fmt) { init (fmt)(); }
		/// Create a string with one argument.
		template <typename T1>
		ostring (Glib::ustring const &fmt, T1 const &a1)
		{ init (fmt)(a1)(); }
		/// Create a string with two arguments.
		template <typename T1, typename T2>
		ostring (Glib::ustring const &fmt, T1 const &a1, T2 const &a2)
		{ init (fmt)(a1)(a2)(); }
		/// Create a string with three arguments.
		template <typename T1, typename T2, typename T3>
		ostring (Glib::ustring const &fmt, T1 const &a1, T2 const &a2,
				T3 const &a3)
		{ init (fmt)(a1)(a2)(a3)(); }
		/// Create a string with four arguments.
		template <typename T1, typename T2, typename T3, typename T4>
		ostring (Glib::ustring const &fmt, T1 const &a1, T2 const &a2,
				T3 const &a3, T4 const &a4)
		{ init (fmt)(a1)(a2)(a3)(a4)(); }
		/// Create a string with five arguments.
		template <typename T1, typename T2, typename T3, typename T4,
			 typename T5>
		ostring (Glib::ustring const &fmt, T1 const &a1, T2 const &a2,
				T3 const &a3, T4 const &a4, T5 const &a5)
		{ init (fmt)(a1)(a2)(a3)(a4)(a5)(); }
		/// Create a string with six arguments.
		template <typename T1, typename T2, typename T3, typename T4,
			 typename T5, typename T6>
		ostring (Glib::ustring const &fmt, T1 const &a1, T2 const &a2,
				T3 const &a3, T4 const &a4, T5 const &a5,
				T6 const &a6)
		{ init (fmt)(a1)(a2)(a3)(a4)(a5)(a6)(); }
		/// Create a string with seven arguments.
		template <typename T1, typename T2, typename T3, typename T4,
			 typename T5, typename T6, typename T7>
		ostring (Glib::ustring const &fmt, T1 const &a1, T2 const &a2,
				T3 const &a3, T4 const &a4, T5 const &a5,
				T6 const &a6, T7 const &a7)
		{ init (fmt)(a1)(a2)(a3)(a4)(a5)(a6)(a7)(); }
		/// Create a string with eight arguments.
		template <typename T1, typename T2, typename T3, typename T4,
			 typename T5, typename T6, typename T7 ,typename T8>
		ostring (Glib::ustring const &fmt, T1 const &a1, T2 const &a2,
				T3 const &a3, T4 const &a4, T5 const &a5,
				T6 const &a6, T7 const &a7, T8 const &a8)
		{ init (fmt)(a1)(a2)(a3)(a4)(a5)(a6)(a7)(a8)(); }
		/// Create a string with nine arguments.
		template <typename T1, typename T2, typename T3, typename T4,
			 typename T5, typename T6, typename T7 ,typename T8,
			 typename T9>
		ostring (Glib::ustring const &fmt, T1 const &a1, T2 const &a2,
				T3 const &a3, T4 const &a4, T5 const &a5,
				T6 const &a6, T7 const &a7, T8 const &a8,
				T9 const &a9)
		{ init (fmt)(a1)(a2)(a3)(a4)(a5)(a6)(a7)(a8)(a9)(); }
	};

	/// Write a variable to a string.
	/** This first read the flags, width and precision, and passes that to write_var (which can be specialized by types).
	 */
	template <typename T> void ostring::write_var_raw (T const &a)
	{
		unsigned width = 0, precision = 0;
		Glib::ustring flags;
		while (pos < format.size ()
				&& (format[pos] < 'a' || format[pos] > 'z')
				&& (format[pos] < 'A' || format[pos] > 'Z')
				&& (format[pos] < '1' || format[pos] > '9'))
			flags += format[pos++];
		while (pos < format.size () && format[pos] >= '0'
				&& format[pos] <= '9')
		{
			width *= 10;
			width += format[pos++] - '0';
		}
		if (pos < format.size () && format[pos] == '.')
		{
			++pos;
			while (pos < format.size () && format[pos] >= '0'
					&& format[pos] <= '9')
			{
				precision *= 10;
				precision += format[pos++] - '0';
			}
		}
		return write_var (a, flags, width, precision);
	}

	/// Write a variable to the string.
	/** This function can be specialized to allow writing custom types using shevek::ostring.
	 */
	template <typename T> void ostring::write_var (T const &a,
			Glib::ustring const &flags,
			unsigned width, unsigned precision)
	{
		(void)flags;
		(void)width;
		(void)precision;
		if (pos < format.size ())
			++pos;
		std::ostringstream s;
		s << a;
		data += s.str ();
	}

	/// Specialization for unsigned short.
	template <> void ostring::write_var <unsigned short> (unsigned short const &a, Glib::ustring const &flags, unsigned width, unsigned precision);
	/// Specialization for short.
	template <> void ostring::write_var <short> (short const &a, Glib::ustring const &flags, unsigned width, unsigned precision);
	/// Specialization for unsigned.
	template <> void ostring::write_var <unsigned> (unsigned const &a, Glib::ustring const &flags, unsigned width, unsigned precision);
	/// Specialization for int.
	template <> void ostring::write_var <int> (int const &a, Glib::ustring const &flags, unsigned width, unsigned precision);
	/// Specialization for Glib::ustring.
	template <> void ostring::write_var <Glib::ustring> (Glib::ustring const &a, Glib::ustring const &flags, unsigned width, unsigned precision);

	/// Not implemented.
	/** For std::string, you should use ri/ostring instead.  So this is declared and not defined.
	 *  It will trigger a compiler error if it is used.
	 */
	template <> void ostring::write_var_raw <std::string> (std::string const &a);
	template <> bool istring::read_var <std::string> (Glib::ustring const &format, std::string &ret, Glib::ustring::size_type &inpos);

	/// shevek::ristring is identical to shevek::istring, but it uses std::string instead of Glib::ustring.
	class ristring
	{
		// The data to parse.
		std::string data;
		// Pointer to data still to be parsed.
		std::stack <std::string::size_type> pos;

		// Read a variable.
		template <typename T>
		bool read_var (std::string const &format, T &ret, std::string::size_type &inpos);

		// Read away whitespace.
		void del_whitespace ();

		// Check for a constant.
		bool read_const (std::string const &format, std::string::size_type &inpos);
	public:
		// Constructors: fill data and make one stack level.
		/// Create a new istring with no data.
		ristring () { pos.push (0); }
		/// Create a new istring with data.
		ristring (std::string const &str) { init (str); }
		/// Set new data to an existing istring.
		void init (std::string const &str) { data = str; while (!pos.empty ()) pos.pop (); pos.push (0); }

		// Stack functions.
		/// Push the current position to the stack so it can be restored later.
		void push () { pos.push (pos.top ()); }
		/// Pop the last pushed position from the stack.
		/** If keep is true or not given, the current position is restored to the last position.
		 *  If it is false, the current position is not changed.
		 */
		int pop (bool keep = false);
		/// Set the current position to 0, but don't change the stack.
		void reset () { pos.top () = 0; }

		/// Get remaining string.
		std::string rest () const { return data.substr (pos.top ()); }

		/// Skip some characters.
		void skip (std::string::size_type p) { pos.top () += p; }

		/// Read a constant string from the input.
		bool operator() (std::string const &format)
		{
			push ();
			std::string::size_type inpos = 0;
			bool ret = read_const (format, inpos)
				&& inpos == format.size ();
			pop (ret);
			return ret;
		}

		/// Read a string containing one argument from the input.
		template <typename T1>
		bool operator() (std::string const &format, T1 &arg1)
		{
			push ();
			std::string::size_type inpos = 0;
			bool ret = read_const (format, inpos)
				&& read_var (format, arg1, inpos)
				&& read_const (format, inpos)
				&& inpos == format.size ();
			pop (ret);
			return ret;
		}

		/// Read a string containing two arguments from the input.
		template <typename T1, typename T2>
		bool operator() (std::string const &format, T1 &arg1,
				T2 &arg2)
		{
			push ();
			std::string::size_type inpos = 0;
			bool ret = read_const (format, inpos)
				&& read_var (format, arg1, inpos)
				&& read_const (format, inpos)
				&& read_var (format, arg2, inpos)
				&& read_const (format, inpos)
				&& inpos == format.size ();
			pop (ret);
			return ret;
		}

		/// Read a string containing three arguments from the input.
		template <typename T1, typename T2, typename T3>
		bool operator() (std::string const &format, T1 &arg1,
				T2 &arg2, T3 &arg3)
		{
			push ();
			std::string::size_type inpos = 0;
			bool ret = read_const (format, inpos)
				&& read_var (format, arg1, inpos)
				&& read_const (format, inpos)
				&& read_var (format, arg2, inpos)
				&& read_const (format, inpos)
				&& read_var (format, arg3, inpos)
				&& read_const (format, inpos)
				&& inpos == format.size ();
			pop (ret);
			return ret;
		}

		/// Read a string containing four arguments from the input.
		template <typename T1, typename T2, typename T3, typename T4>
		bool operator() (std::string const &format, T1 &arg1,
				T2 &arg2, T3 &arg3, T4 &arg4)
		{
			push ();
			std::string::size_type inpos = 0;
			bool ret = read_const (format, inpos)
				&& read_var (format, arg1, inpos)
				&& read_const (format, inpos)
				&& read_var (format, arg2, inpos)
				&& read_const (format, inpos)
				&& read_var (format, arg3, inpos)
				&& read_const (format, inpos)
				&& read_var (format, arg4, inpos)
				&& read_const (format, inpos)
				&& inpos == format.size ();
			pop (ret);
			return ret;
		}

		/// Read a string containing five arguments from the input.
		template <typename T1, typename T2, typename T3, typename T4,
			 typename T5>
		bool operator() (std::string const &format, T1 &arg1,
				T2 &arg2, T3 &arg3, T4 &arg4, T5 &arg5)
		{
			push ();
			std::string::size_type inpos = 0;
			bool ret = read_const (format, inpos)
				&& read_var (format, arg1, inpos)
				&& read_const (format, inpos)
				&& read_var (format, arg2, inpos)
				&& read_const (format, inpos)
				&& read_var (format, arg3, inpos)
				&& read_const (format, inpos)
				&& read_var (format, arg4, inpos)
				&& read_const (format, inpos)
				&& read_var (format, arg5, inpos)
				&& read_const (format, inpos)
				&& inpos == format.size ();
			pop (ret);
			return ret;
		}

		/// Read a string containing six arguments from the input.
		template <typename T1, typename T2, typename T3, typename T4,
			 typename T5, typename T6>
		bool operator() (std::string const &format, T1 &arg1,
				T2 &arg2, T3 &arg3, T4 &arg4, T5 &arg5,
				T6 &arg6)
		{
			push ();
			std::string::size_type inpos = 0;
			bool ret = read_const (format, inpos)
				&& read_var (format, arg1, inpos)
				&& read_const (format, inpos)
				&& read_var (format, arg2, inpos)
				&& read_const (format, inpos)
				&& read_var (format, arg3, inpos)
				&& read_const (format, inpos)
				&& read_var (format, arg4, inpos)
				&& read_const (format, inpos)
				&& read_var (format, arg5, inpos)
				&& read_const (format, inpos)
				&& read_var (format, arg6, inpos)
				&& read_const (format, inpos)
				&& inpos == format.size ();
			pop (ret);
			return ret;
		}

		/// Read a string containing seven arguments from the input.
		template <typename T1, typename T2, typename T3, typename T4,
			 typename T5, typename T6, typename T7>
		bool operator() (std::string const &format, T1 &arg1,
				T2 &arg2, T3 &arg3, T4 &arg4, T5 &arg5,
				T6 &arg6, T7 &arg7)
		{
			push ();
			std::string::size_type inpos = 0;
			bool ret = read_const (format, inpos)
				&& read_var (format, arg1, inpos)
				&& read_const (format, inpos)
				&& read_var (format, arg2, inpos)
				&& read_const (format, inpos)
				&& read_var (format, arg3, inpos)
				&& read_const (format, inpos)
				&& read_var (format, arg4, inpos)
				&& read_const (format, inpos)
				&& read_var (format, arg5, inpos)
				&& read_const (format, inpos)
				&& read_var (format, arg6, inpos)
				&& read_const (format, inpos)
				&& read_var (format, arg7, inpos)
				&& read_const (format, inpos)
				&& inpos == format.size ();
			pop (ret);
			return ret;
		}

		/// Read a string containing eight arguments from the input.
		template <typename T1, typename T2, typename T3, typename T4,
			 typename T5, typename T6, typename T7, typename T8>
		bool operator() (std::string const &format, T1 &arg1,
				T2 &arg2, T3 &arg3, T4 &arg4, T5 &arg5,
				T6 &arg6, T7 &arg7, T8 &arg8)
		{
			push ();
			std::string::size_type inpos = 0;
			bool ret = read_const (format, inpos)
				&& read_var (format, arg1, inpos)
				&& read_const (format, inpos)
				&& read_var (format, arg2, inpos)
				&& read_const (format, inpos)
				&& read_var (format, arg3, inpos)
				&& read_const (format, inpos)
				&& read_var (format, arg4, inpos)
				&& read_const (format, inpos)
				&& read_var (format, arg5, inpos)
				&& read_const (format, inpos)
				&& read_var (format, arg6, inpos)
				&& read_const (format, inpos)
				&& read_var (format, arg7, inpos)
				&& read_const (format, inpos)
				&& read_var (format, arg8, inpos)
				&& read_const (format, inpos)
				&& inpos == format.size ();
			pop (ret);
			return ret;
		}

		/// Read a variable from given data and return it.
		/** This allows using a shevek::istring in an expression without the need to create a new variable for it.
		 *  If the input doesn't match the format, def is returned.
		 */
		template <typename T>
		static T direct (std::string const &data,
				std::string const &format,
				T def = T ())
		{
			T ret;
			istring tmp (data);
			if (!tmp (format, ret))
				ret = def;
			return ret;
		}
	};

	/// Template function for reading any type.
	/** The default implementation uses a std::istringstream extractor.
	 *  This function can be specialized by a program to allow reading custom types with a shevek::ristring.
	 */
	template <typename T>
	bool ristring::read_var (std::string const &format, T &ret, std::string::size_type &inpos)
	{
		(void *)&format;
		++inpos;
		std::istringstream s (data.substr (pos.top ()));
		s >> ret;
		std::streampos p = s.tellg ();
		if (!s || p < 0)
			return false;
		pos.top () += p;
		return true;
	}

	/// Specialization of read_var for double.
	template <>
	bool ristring::read_var <double> (std::string const &format,
			double &ret, std::string::size_type &inpos);

	/// Specialization of read_var for float.
	template <>
	bool ristring::read_var <float> (std::string const &format,
			float &ret, std::string::size_type &inpos);

	/// Specialization of read_var for std::string.
	template <>
	bool ristring::read_var <std::string> (std::string const &format,
			std::string &ret, std::string::size_type &inpos);

	/// Specialization of read_var for int.
	template <>
	bool ristring::read_var <int> (std::string const &format,
			int &ret, std::string::size_type &inpos);

	/// Specialization of read_var for unsigned.
	template <>
	bool ristring::read_var <unsigned> (std::string const &format,
			unsigned &ret, std::string::size_type &inpos);

	/// shevek::rostring is identical to shevek::ostring, but it uses std::string instead of Glib::ustring.
	class rostring
	{
		std::string data;
		std::string format;
		std::string::size_type pos;
		void write_const ();
		template <typename T> void write_var (T const &a,
				std::string const &flags,
				unsigned width, unsigned precision);
		template <typename T> void write_var_raw (T const &a);
		rostring &init (std::string const &f);
		template <typename T> rostring &operator ()(T const &a)
		{ write_const (); write_var_raw (a); return *this; }
		rostring &operator ()();
	public:
		/// Use the result as a string.
		operator std::string () const { return data; }
		/// Use the result as a string.
		std::string operator+ (std::string const &that) const { return data + that; }
		/// Send the result to an ostream.
		friend std::ostream &operator<< (std::ostream &s, rostring const &o) { return s << o.data; }
		/// Create a string with a constant format.
		rostring (std::string const &fmt)
		{ init (fmt)(); }
		/// Create a string with one argument.
		template <typename T1>
		rostring (std::string const &fmt, T1 const &a1)
		{ init (fmt)(a1)(); }
		/// Create a string with two arguments.
		template <typename T1, typename T2>
		rostring (std::string const &fmt, T1 const &a1, T2 const &a2)
		{ init (fmt)(a1)(a2)(); }
		/// Create a string with three arguments.
		template <typename T1, typename T2, typename T3>
		rostring (std::string const &fmt, T1 const &a1, T2 const &a2, T3 const &a3)
		{ init (fmt)(a1)(a2)(a3)(); }
		/// Create a string with four arguments.
		template <typename T1, typename T2, typename T3, typename T4>
		rostring (std::string const &fmt, T1 const &a1, T2 const &a2, T3 const &a3, T4 const &a4)
		{ init (fmt)(a1)(a2)(a3)(a4)(); }
		/// Create a string with five arguments.
		template <typename T1, typename T2, typename T3, typename T4, typename T5>
		rostring (std::string const &fmt, T1 const &a1, T2 const &a2, T3 const &a3, T4 const &a4, T5 const &a5)
		{ init (fmt)(a1)(a2)(a3)(a4)(a5)(); }
		/// Create a string with six arguments.
		template <typename T1, typename T2, typename T3, typename T4, typename T5, typename T6>
		rostring (std::string const &fmt, T1 const &a1, T2 const &a2, T3 const &a3, T4 const &a4, T5 const &a5, T6 const &a6)
		{ init (fmt)(a1)(a2)(a3)(a4)(a5)(a6)(); }
		/// Create a string with seven arguments.
		template <typename T1, typename T2, typename T3, typename T4, typename T5, typename T6, typename T7>
		rostring (std::string const &fmt, T1 const &a1, T2 const &a2, T3 const &a3, T4 const &a4, T5 const &a5, T6 const &a6, T7 const &a7)
		{ init (fmt)(a1)(a2)(a3)(a4)(a5)(a6)(a7)(); }
		/// Create a string with eight arguments.
		template <typename T1, typename T2, typename T3, typename T4, typename T5, typename T6, typename T7 ,typename T8>
		rostring (std::string const &fmt, T1 const &a1, T2 const &a2, T3 const &a3, T4 const &a4, T5 const &a5, T6 const &a6, T7 const &a7, T8 const &a8)
		{ init (fmt)(a1)(a2)(a3)(a4)(a5)(a6)(a7)(a8)(); }
		/// Create a string with nine arguments.
		template <typename T1, typename T2, typename T3, typename T4, typename T5, typename T6, typename T7 ,typename T8, typename T9>
		rostring (std::string const &fmt, T1 const &a1, T2 const &a2, T3 const &a3, T4 const &a4, T5 const &a5, T6 const &a6, T7 const &a7, T8 const &a8, T9 const &a9)
		{ init (fmt)(a1)(a2)(a3)(a4)(a5)(a6)(a7)(a8)(a9)(); }
	};

	/// Function to write a variable to the string.
	/** Normally, you should specialize write_var, not this function.
	 */
	template <typename T> void rostring::write_var_raw (T const &a)
	{
		unsigned width = 0, precision = 0;
		std::string flags;
		while (pos < format.size ()
				&& (format[pos] < 'a' || format[pos] > 'z')
				&& (format[pos] < 'A' || format[pos] > 'Z')
				&& (format[pos] < '1' || format[pos] > '9'))
			flags += format[pos++];
		while (pos < format.size () && format[pos] >= '0'
				&& format[pos] <= '9')
		{
			width *= 10;
			width += format[pos++] - '0';
		}
		if (pos < format.size () && format[pos] == '.')
		{
			++pos;
			while (pos < format.size () && format[pos] >= '0'
					&& format[pos] <= '9')
			{
				precision *= 10;
				precision += format[pos++] - '0';
			}
		}
		return write_var (a, flags, width, precision);
	}

	/// Function to write a variable to the string.
	/** This function can be specialized to allow writing custom types using shevek::rostring.
	 */
	template <typename T> void rostring::write_var (T const &a, std::string const &flags, unsigned width, unsigned precision)
	{
		(void)flags;
		(void)width;
		(void)precision;
		if (pos < format.size ())
			++pos;
		std::ostringstream s;
		s << a;
		data += s.str ();
	}

	/// Specialization for unsigned.
	/** This allows printing in hexadecimal and inserting 0's on the left.
	 */
	template <> void rostring::write_var <unsigned> (unsigned const &a, std::string const &flags, unsigned width, unsigned precision);
	/// Specialization for int.
	/** This allows printing in hexadecimal and inserting 0's on the left.
	 */
	template <> void rostring::write_var <int> (int const &a, std::string const &flags, unsigned width, unsigned precision);

	/// Not implemented.
	/** For Glib::ustring, you should use i/ostring instead.  So this is declared and not defined.
	 *  It will trigger a compiler error if it is used.
	 */
	template <> void rostring::write_var_raw <Glib::ustring> (Glib::ustring const &a);
	template <> bool ristring::read_var <Glib::ustring> (std::string const &format, Glib::ustring &ret, std::string::size_type &inpos);
}

#endif
