#include <u.h>
#include <libc.h>
#include <auth.h>
#include <ctype.h>
#include "dat.h"
#include "fns.h"

Segment segs[3];

void *
emalloc(ulong n)
{
	void *v;
	
	v = malloc(n);
	if(v == nil) sysfatal("malloc: %r");
	memset(v, 0, n);
	setmalloctag(v, getcallerpc(&n));
	return v;
}

enum {
	OMAGIC = 0407,
	NMAGIC = 0410,
	ZMAGIC = 0413,
};

static int
readn32(int fd, u32int *l, int sz)
{
	static uchar buf[8192];
	uchar *p;
	int n, rc;
	
	while(sz > 0){
		n = 8192;
		if(n > sz) n = sz;
		rc = readn(fd, buf, n);
		if(rc < 0) return -1;
		if(rc < n){
			werrstr("unexpected eof");
			return -1;
		}
		sz -= n;
		p = buf;
		for(; n >= 4; n -= 4){
			*l++ = U32(p);
			p += 4;
		}
		switch(n){
		case 1: *l = p[0]; break;
		case 2: *l = p[0] | p[1] << 8; break;
		case 3: *l = p[0] | p[1] << 8 | p[2] << 16; break;
		}
	}
	return 0;
}

static void
setupstack(char **argv)
{
	u32int *nargv;
	int i, j;
	int argc;

	r[14] = 0x7ffff400;
	#define push32(x) { r[14] -= 4; memwrite(r[14], x, -1); }
	for(argc = 0; argv[argc] != nil; argc++)
		;
	nargv = emalloc(sizeof(u32int) * argc);
	for(i = argc; --i >= 0; ){
		r[14] -= strlen(argv[i]) + 1;
		nargv[i] = r[14];
		for(j = 0; argv[i][j] != 0; j++)
			writem(r[14] + j, argv[i][j], 0);
	}
	r[14] &= -4;
	push32(0);
	push32(0);
	push32(0);
	for(i = argc; --i >= 0; )
		push32(nargv[i]);
	push32(argc);
	free(nargv);
}

static int
shload(int fd, char **argv, char **envp)
{
	char buf[256];
	char *s, *a;
	char *p;
	int rc;
	char **nargv, **pp;
	int n;
	
	rc = read(fd, buf, sizeof(buf) - 1);
	if(rc <= 0){
		werrstr("invalid magic");
		return -1;
	}
	close(fd);
	buf[rc] = 0;
	p = strchr(buf, '\n');
	if(p == nil) *p = 0;
	p = buf;
	while(isspace(*p)) p++;
	s = p;
	while(*p != 0 && !isspace(*p)) p++;
	if(*p != 0){
		*p = 0;
		while(isspace(*p)) p++;
		if(*p != 0){
			a = p;
			while(*p != 0 && !isspace(*p)) p++;
		}else a = nil;
	}else a = nil;
	for(n = 0; argv[n] != nil; n++)
		;
	nargv = emalloc((n + 3) * sizeof(char *));
	pp = nargv;
	*pp++ = s;
	if(a != nil) *pp++ = a;
	while(n--)
		*pp++ = *argv++;
	load(s, nargv, envp);
	free(nargv);
	return 0;
}

int
load(char *file, char **argv, char **envp)
{
	uchar hdr[32];
	int fd;
	u32int hmagic, htext, hdata, hbss, hentry;
	
	fd = open(file, OREAD);
	if(fd < 0) return -1;
	if(readn(fd, hdr, 2) < 2) return -1;
	if(hdr[0] == '#' && hdr[1] == '!')
		return shload(fd, argv, envp);
	if(readn(fd, hdr + 2, 30) < 30) return -1;
	hmagic = U32(&hdr[0]);
	htext = U32(&hdr[4]);
	hdata = U32(&hdr[8]);
	hbss = U32(&hdr[12]);
	hentry = U32(&hdr[20]);
	switch(hmagic){
	case ZMAGIC: case OMAGIC: case NMAGIC: break;
	default:
		werrstr("invalid magic %.6o", hmagic);
		return -1;
	}
	free(segs[0].data);
	free(segs[1].data);
	free(segs[2].data);
	segs[0].start = 0;
	segs[0].size = htext;
	segs[0].data = emalloc(-(-htext & -4));
	segs[1].start = -(-htext & -1024);
	segs[1].size = hdata + hbss;
	segs[1].data = emalloc(-(-(hdata + hbss) & -4));
	segs[2].start = 0x7ffff400 - STACKSIZE;
	segs[2].size = STACKSIZE;
	segs[2].data = emalloc(STACKSIZE);
	if(hmagic != OMAGIC)
		segs[0].flags = SEGRO;
	if(hmagic == ZMAGIC)
		seek(fd, 1024, 0);
	if(readn32(fd, segs[0].data, htext) < 0) exits(smprint("%r"));
	if(readn32(fd, segs[1].data, hdata) < 0) exits(smprint("%r"));
	close(fd);
	memset(r, 0, sizeof(r));
	r[15] = hentry + 2;
	setupstack(argv);
	return 0;
}

static void
usage(void)
{
	fprint(2, "usage: %s file\n", argv0);
	exits("usage");
}

void
main(int argc, char **argv)
{
	extern void fptest(void);
	
	rfork(RFNAMEG);
	fptest();
	ARGBEGIN{
	case 'N':
		if(addns(nil, EARGF(usage())) < 0)
			sysfatal("addns: %r");
		break;
	default: usage();
	}ARGEND;
	
	if(argc < 1) usage();
	sysinit();
	if(load(argv[0], argv, nil) < 0) sysfatal("load: %r");
	for(;;) step();
}
