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

static void  append(const char *path);
static void  catio(FILE *in, FILE *out, long n);
static void  fatal(const char *s);
static void  extract(void);
static char *strvstr(char **strv, const char *str);

static struct Entry {
	char path[257];
	char size[15];
} ent;

int
main(int argc, char *argv[]) {
	int i;

	if(argc < 2 || strlen(argv[1]) != 1) {
usage:
		fputs("usage: wrap {cdrtx} [files...]\n", stderr);
		exit(1);
	}
	switch(*argv[1]) {
	case 'd':
	case 'r':
		while(fread(&ent, sizeof ent, 1, stdin))
			if(!strvstr(argv + 2, ent.path)) {
				fwrite(&ent, sizeof ent, 1, stdout);
				catio(stdin, stdout, atol(ent.size));
			}
			else
				catio(stdin, NULL, atol(ent.size));
		if(*argv[1] != 'r')
			break;
	case 'c':
		for(i = 2; i < argc; i++)
			append(argv[i]);
		break;
	case 't':
		while(fread(&ent, sizeof ent, 1, stdin)) {
			if(argc == 2 || strvstr(argv + 2, ent.path))
				printf("%s\n", ent.path);
			catio(stdin, NULL, atol(ent.size));
		}
		break;
	case 'x':
		while(fread(&ent, sizeof ent, 1, stdin))
			if(argc == 2 || strvstr(argv + 2, ent.path))
				extract();
		break;
	default:
		goto usage;
	}
	return EXIT_SUCCESS;
}

void
append(const char *path) {
	struct stat st;
	FILE *fp;

	if(stat(path, &st) < 0)
		fatal(path);
	if(!(fp = fopen(path, "r")))
		fatal(path);
	memset(&ent, 0, sizeof ent);
	strncpy(ent.path, path, sizeof ent.path - 1);
	snprintf(ent.size, sizeof ent.size - 1, "%lu", st.st_size);

	fwrite(&ent, sizeof ent, 1, stdout);
	catio(fp, stdout, st.st_size);
	fclose(fp);
}

void
catio(FILE *in, FILE *out, long n) {
	char buf[8192];
	long m;

	for(; (m = (n < sizeof buf) ? n : sizeof buf); n -= m)
		if(!fread(buf, m, 1, in))
			fatal("read error");
		else if(out && !fwrite(buf, m, 1, out))
			fatal("write error");
}

void
extract(void) {
	char *p = ent.path;
	FILE *fp;

	while((p = strchr(p + 1, '/'))) {
		*p = '\0';
		if(mkdir(ent.path, 0755) < 0 && errno != EEXIST)
			fatal(ent.path);
		*p = '/';
	}
	if(!(fp = fopen(ent.path, "w")))
		fatal(ent.path);
	catio(stdin, fp, atol(ent.size));
	fclose(fp);
}

void
fatal(const char *s) {
	fprintf(stderr, "wrap: %s: %s\n", s, strerror(errno));
	exit(EXIT_FAILURE);
}

char *
strvstr(char **strv, const char *str) {
	for(; *strv; strv++)
		if(!strcmp(*strv, str))
			return *strv;
	return NULL;
}
