/*
 * Copyright (C) 2003-2009 FAUmachine Team <info@faumachine.org>.
 * This program is free software. You can redistribute it and/or modify it
 * under the terms of the GNU General Public License, either version 2 of
 * the License, or (at your option) any later version. See COPYING.
 */

#include "../config.h"

#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>

#ifndef UMSTORAGE_COW_BLOCKSIZE
#define UMSTORAGE_COW_BLOCKSIZE 4096
#endif
#ifndef PATH_MAX /* WTF? */
#define PATH_MAX 256
#endif

static void
error(const char *msg) __attribute__((__noreturn__));
static void
error(const char *msg)
{
	perror(msg);
	exit(1);
}

int
main(int argc, char *argv[])
{
	FILE *cow, *map, *media, *target;
	char name[PATH_MAX];
	char *src;
	char *tgt;

	if(argc!=3) {
		fprintf(stderr, "usage: %s src-media target-file\n", argv[0]);
		fprintf(stderr, "src-media{,.cow,.map} will be merged into one self-containing image in target-file\n");
		exit(1);
	}
	src = argv[1];
	tgt = argv[2];

	snprintf(name, sizeof(name), "%s.cow", src);
	cow = fopen(name, "r");
	if(!cow) error("open cow");
	snprintf(name, sizeof(name), "%s.map", src);
	map = fopen(name, "r");
	if(!map) error("open map");
	media = fopen(src, "r");
	if(!media) error("open media");

	target = fopen(tgt, "w");
	if(!target) error("open target");

	while( !feof(map) ) {
		unsigned char block_map;
		unsigned char mask;
		int i;
		int ret;
		char buffer[UMSTORAGE_COW_BLOCKSIZE];
		size_t sz;

		sz = fread(&block_map, sizeof(block_map), 1, map);
		if(feof(map)) break;
		if(ferror(map)) error("read map");

		for(i=0, mask=1; i<sizeof(block_map)*8; i++, mask<<=1) {
			if( block_map & mask ) { /* use cow file */
				ret = fread(buffer, sizeof(buffer), 1, cow);
				if(!ret && feof(cow)) goto finished;
				else if (!ret) error("read cow");
				ret = fwrite(buffer, sizeof(buffer), 1, target);
				if(!ret) error("write target");
				ret = fseek(media, sizeof(buffer), SEEK_CUR);
				if(ret) error("seek media");
			} else { /* use media file */
				ret = fread(buffer, sizeof(buffer), 1, media);
				if(!ret && feof(media)) goto finished;
				else if(!ret) error("read media");
				ret = fwrite(buffer, sizeof(buffer), 1, target);
				if(!ret) error("write target");
				ret = fseek(cow, sizeof(buffer), SEEK_CUR);
				if(ret) error("seek cow");
			}
		}
	}

finished:
	fclose(target);
	fclose(map);
	fclose(media);
	fclose(cow);

	return 0;
}
