#include <9pm/u.h>
#include <9pm/libc.h>
#include <9pm/ns.h>
#include <9pm/thread.h>
#include <9pm/threadimpl.h>

Channel *_threadwaitchan;

void
procexec(Channel *pidc, char *f, char *args[])
{
	char *q;
	int l, n, pid, s;
	Dir *d;
	Execproc *ep;
	Tproc *p;

	_threaddebug(DBGPROC, "procexec %s", f);
	/* make sure exec is likely to succeed before tossing thread state */
	d = dirstat(f);
	if(d == nil || (d->mode & DMDIR)/* || access(f, AEXEC) < 0*/) {
		_threaddebug(DBGPROC, "cannot find program %s", f);
		free(d);
bad:	if (pidc) sendul(pidc, ~0);
		return;
	}
	free(d);
	p = _threadgetproc();
	if(p->threads.head != p->curthread || p->threads.head->nextt != nil)
		goto bad;

	/* committed to exec-ing */
	_threadfdcoherence();

	n = 0;
	while (args[n++])
		;
	ep = &p->execproc;
	q = ep->data;
	s = sizeof(ep->data);
	if ((l = n*sizeof(char *)) < s) {
		_threaddebug(DBGPROC, "args on stack, %d pointers", n);
		ep->arg = (char **)q;
		q += l;
		s -= l;
	} else
		ep->arg = _threadmalloc(l, 0);	/* will be leaked */
	if ((l = strlen(f) + 1) < s) {
		ep->file = q;
		strcpy(q, f);
		_threaddebug(DBGPROC, "file on stack, %s", q);
		q += l;
		s -= l;
	} else
		ep->file = strdup(f);	/* will be leaked */
	ep->arg[--n] = nil;
	while (--n >= 0)
		if ((l = strlen(args[n]) + 1) < s) {
			ep->arg[n] = q;
			strcpy(q, args[n]);
			_threaddebug(DBGPROC, "arg on stack, %s", q);
			q += l;
			s -= l;
		} else
			ep->arg[n] = strdup(args[n]);	/* will be leaked */
	p->arg = ep;
	pid = _threadswtch(p->curthread->env, p->oldenv, DOEXEC);
	close(0);
	close(1);
	/* 3 and up are already closed from fdcoherence */
	if(pidc != nil)
		sendul(pidc, pid);
	_procexecwait(pid);
	_threaddebug(DBGPROC, "Exiting (exec)");
	_threadgetproc()->nowait = 1;
	threadexits("libthread procexec");
}

void
procexecl(Channel *pidc, char *f, ...)
{
	procexec(pidc, f, &f+1);
}

Channel*
threadwaitchan(void)
{
	_threadwaitchan = chancreate(sizeof(Waitmsg*), 0);
	return _threadwaitchan;
}
