#include <errno.h>
#include <inttypes.h>
#include <stdlib.h>
#include <stdio.h>
#include <sys/stat.h>

#define BLOCK_ALIGN (4096)
unsigned char zeros[BLOCK_ALIGN] = { 0 };

static void die(const char *msg)
{
	perror(msg);
	exit(EXIT_FAILURE);
}

static size_t written = 0;

static void writeBuffer(void *data, size_t size)
{
	if (fwrite(data, 1, size, stdout) != size) {
		die("fwrite");
	}
	written += size;
}

static void writeInt(uint32_t i)
{
	writeBuffer(&i, sizeof(i));
}

static void fill()
{
	size_t rem = BLOCK_ALIGN - (written % BLOCK_ALIGN);
	if (rem == BLOCK_ALIGN) {
		return;
	}

	writeBuffer(zeros, rem);
	written = 0;
}

static uint32_t getFileSize(const char *filename)
{
	struct stat sb;
	if (stat(filename, &sb) != 0) {
		die("stat");
	}

	if (sb.st_size >= UINT32_MAX) {
		errno = EFBIG;
		die("stat");
	}
	return sb.st_size;
}

static unsigned char *getFileContent(const char *filename)
{
	uint32_t s = getFileSize(filename);
	unsigned char *contents = (unsigned char *) malloc(s);
	if (contents == NULL) {
		die("malloc");
	}

	FILE *f = fopen(filename, "r");
	if (f == NULL) {
		die("fopen");
	}

	uint32_t absorbed = fread(contents, sizeof(unsigned char), s, f);
	if (absorbed != s) {
		die("fread");
	}

	int status = fclose(f);
	if (status != 0) {
		die("fclose");
	}
	return contents;
}

int main(int argc, char *argv[])
{
	if (argc < 2) {
		fprintf(stderr, "usage: %s <list of flat binaries>\n", argv[0]);
		return EXIT_FAILURE;
	}

	argc = argc - 1;
	argv = argv + 1;

	//header
	writeInt(argc);
	for (int i = 0; i < argc; ++i) {
		writeInt(getFileSize(argv[i]));
	}
	fill();

	//files
	for (int i = 0; i < argc; ++i) {
		uint32_t size = getFileSize(argv[i]);
		uint8_t *buf = getFileContent(argv[i]);
		writeBuffer(buf, size);
		fill();
		free(buf);
	}

	fflush(stdout);
	fclose(stdout);
	return EXIT_SUCCESS;
}

