#include "safe_save_file.h"

#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>

#include <iostream>
#include <fstream>

#include <coral/lib/cpp_posix.h>
#include <coral/lib/cpp_errno.h>

#include <stdexcept>

namespace coral {
	void safe_save_file(const std::string& filename,
			    save_file& output_object)
		throw (std::exception) {
		/*
		 * Do monkey dance when saving unless
		 * we're saving to a non-regular file.
		 */
		bool do_monkey_dance = true;
		struct stat statbuf;
		int err = lstat(filename.c_str(), &statbuf);
		if (!err && !S_ISREG(statbuf.st_mode)) {
			do_monkey_dance = false;
		}

		// BUG security

		if (!do_monkey_dance) {
			std::ofstream f(filename.c_str());

			output_object(f);

			f.close();
			if (!f.fail())
				return;
			std::string s("Unrecoverable output error occured when saving");
			s += filename;
			s += "!";
			throw std::runtime_error(s);
		}


		std::string tempfilename(filename);
		tempfilename += "XXXXXX";

		{
			std::auto_ptr<std::streambuf> buf;
			buf = cpp_mkstemp(tempfilename, tempfilename);

			std::ostream out(buf.get());
			output_object(out);

			// BUG need to close out, I wonder if flush
			// is enough?
			out.flush();

			if (out.fail()) {
				std::string s("Unrecoverable output error occured when saving ");
				s += tempfilename + " (for file ";
				s += filename + ")!";
				throw std::runtime_error(s);
			}
		}
		/*
		 * Rename temporary file to real name
		 */
		cpp_file_rename(tempfilename, filename);
	}
}
