#include	"mk.h"

Channel *wchan;

char 	*shell =	"/bin/rc";
char 	*shellname =	"rc";

static	int	nexec, nwait;

static	char		envsep;

static	Word	*encodenulls(char*, int);

void
readenv(void)
{
	char *p;
	int envf, f;
	Dir *e;
	char nam[1024];
	int i, n, len;
	Word *w;

	wchan = threadwaitchan();
	if(1) //pm_conf.ostype == PmOsPlan9)
		envsep = '\0';
	else
		envsep = '\01';
	
//	rfork(RFENVG);	/*  use copy of the current environment variables */

	envf = open("/env", OREAD);
	if(envf < 0)
		return;
	while((n = dirread(envf, &e)) > 0){
		for(i = 0; i < n; i++){
			len = e[i].length;
				/* don't import funny names, NULL values,
				 * or internal mk variables
				 */
			if(len <= 0 || *shname(e[i].name) != '\0')
				continue;
			if (symlook(e[i].name, S_INTERNAL, 0))
				continue;
			sprint(nam, "/env/%s", e[i].name);
			f = open(nam, OREAD);
			if(f < 0)
				continue;
			p = Malloc(len+1);
			if(read(f, p, len) != len){
				perror(nam);
				close(f);
				continue;
			}
			close(f);
			if (p[len-1] == 0)
				len--;
			else
				p[len] = 0;
			w = encodenulls(p, len);
			free(p);
			p = strdup(e[i].name);
			setvar(p, (void *) w);
			symlook(p, S_EXPORTED, (void*)"")->value = (void*)"";
		}
		free(e);
	}
	close(envf);
}

/* break string of values into words at 01's or nulls*/
static Word *
encodenulls(char *s, int n)
{
	Word *w, *head;
	char *cp;

	head = w = 0;
	while (n-- > 0) {
		for (cp = s; *cp && *cp != envsep; cp++)
				n--;
		*cp = 0;
		if (w) {
			w->next = newword(s);
			w = w->next;
		} else
			head = w = newword(s);
		s = cp+1;
	}
	if (!head)
		head = newword("");
	return head;
}

/* as well as 01's, change blanks to nulls, so that rc will
 * treat the words as separate arguments
 */
void
exportenv(Envy *e)
{
	int f, n, hasvalue, first;
	Word *w;
	Symtab *sy;
	char nam[256];

	for(;e->name; e++){
		sy = symlook(e->name, S_VAR, 0);
		if (e->values == 0 || e->values->s == 0 || e->values->s[0] == 0)
			hasvalue = 0;
		else
			hasvalue = 1;
		if(sy == 0 && !hasvalue)	/* non-existant null symbol */
			continue;
		sprint(nam, "/env/%s", e->name);
		if (sy != 0 && !hasvalue){
			remove(nam);
			e->values = 0;
			symdel(e->name, S_VAR);
			continue;
		}
	
		f = create(nam, OWRITE, 0666L);
		if(f < 0) {
			fprint(2, "can't create %s, f=%d\n", nam, f);
			perror(nam);
			continue;
		}
		first = 1;
		for (w = e->values; w; w = w->next) {
			n = strlen(w->s);
			if (n) {
				if(first)
					first = 0;
				else{
					if (write (f, &envsep, 1) != 1)
						perror(nam);
				}
				if (write(f, w->s, n) != n)
					perror(nam);
			}
		}
		close(f);
	}
}

int
waitfor(char *msg)
{
	Waitmsg *w;
	int pid;

	if(nwait >= nexec)
		return -1;

Again:
	if((w=recvp(wchan)) == nil)
		return -1;
	strecpy(msg, msg+ERRMAX, w->msg);
	pid = w->pid;
	free(w);
	if(strcmp(msg, "libthread procexec") == 0)
		goto Again;
	nwait++;
	return pid;
}

void
expunge(int pid, char *msg)
{
	postnote(PNPROC, pid, msg);
}

static void
writecmd(void *a)
{
	struct {char *cmd; int fd;} *arg;
	char *p, *e;
	int n;

	threadnowait();
	arg = a;
	p = arg->cmd;
	e = p+strlen(p);
	while(p < e){
		n = write(arg->fd, p, e-p);
		if(n <= 0)
			break;
		p += n;
	}	
	free(arg->cmd);
	close(arg->fd);
	free(arg);
}

static void
execproc(void *a)
{
	struct { char *cmd; char *argv[4]; int fd[2]; Channel *cpid; } *arg;

	arg = a;
	rfork(RFFDG);
	dup(arg->fd[0], 0);
	if(arg->fd[1] != 1)
		dup(arg->fd[1], 1);
	nexec++;
	procexec(arg->cpid, arg->cmd, arg->argv);
	threadexits(nil);
}

int
execsh(char *args, char *cmd, Bufblock *buf, Envy *e)
{
	int tot, n, in[2], out[2];
	int i, pid;
	struct { char *cmd; int fd;} *wt;
	struct { char *cmd; char *argv[4]; int fd[2]; Channel *cpid; } et;

	out[1] = 1;
	if(buf && pipe(out) < 0) {
		perror("pipe");
		Exit();
	}

	if(pipe(in) < 0){
		perror("pipe");
		Exit();
	}

	wt = malloc(sizeof(*wt));
	wt->cmd = strdup(cmd);
	wt->fd = in[1];
	proccreate(writecmd, wt, 8192);

	if(e)
		exportenv(e);

	i=0;
	et.argv[i++] = shellname;
	if(shflags)
		et.argv[i++] = shflags;
	if(args) 
		et.argv[i++] = args;
	et.argv[i] = 0;
	et.cmd = shell;

	et.fd[0] = in[0];
	et.fd[1] = out[1];
	et.cpid = chancreate(sizeof(ulong), 0);
	if(proccreate(execproc, &et, 8192) < 0)
		sysfatal("proccreate %s failed: %r", shell);
	pid = recvul(et.cpid);
	if(pid == ~0)
		sysfatal("procexec failed\n");
	chanfree(et.cpid);
	close(in[0]);

	if(buf){
		close(out[1]);
		tot = 0;
		for(;;){
			if (buf->current >= buf->end)
				growbuf(buf);
			n = read(out[0], buf->current, buf->end-buf->current);
			if(n <= 0)
				break;
			buf->current += n;
			tot += n;
		}
		if (tot && buf->current[-1] == '\n')
			buf->current--;
		close(out[0]);
	}
	return pid;
}

static void
pipeexecproc(void *a)
{
	struct { char *cmd; int fd[2]; Channel *cpid; } *arg;

	arg = a;
	rfork(RFFDG);
	if(arg->fd[0] != 0)
		dup(arg->fd[0], 0);
	if(arg->fd[1] != 1)
		dup(arg->fd[1], 1);
	nexec++;
	if(shflags)
		procexecl(arg->cpid, shell, shellname, shflags, "-c", arg->cmd, nil);
	else
		procexecl(arg->cpid, shell, shellname, "-c", arg->cmd, nil);
	threadexits(nil);
}

int
pipecmd(char *cmd, Envy *e, int *fd)
{
	int pid;
	struct { char *cmd; int fd[2]; Channel *cpid; } a;

	if(DEBUG(D_EXEC))
		fprint(1, "pipecmd='%s'\n", cmd);/**/

	if(e)
		exportenv(e);
	a.fd[0] = 0;
	a.fd[1] = 1;
	if(fd && pipe(a.fd) < 0){
		perror("pipe");
		Exit();
	}
	a.cpid = chancreate(sizeof(ulong), 0);
	if(proccreate(pipeexecproc, &a, 8192) < 0)
		sysfatal("proccreate failed: %r\n");
	pid = recvul(a.cpid);
	if(pid == ~0)
		sysfatal("procexec failed\n");
	if(fd){
		close(fd[1]);
		*fd = fd[0];
	}
	return pid;
}

void
Exit(void)
{
	threadexitsall("error");
}

int
notifyf(void *a, char *msg)
{
	static int nnote;

	USED(a);
	if(++nnote > 100){	/* until andrew fixes his program */
		fprint(2, "mk: too many notes\n");
		notify(0);
		abort();
	}
	if(strcmp(msg, "interrupt")!=0 && strcmp(msg, "hangup")!=0)
		return 0;
	killchildren(msg);
	return -1;
}

void
catchnotes()
{
	atnotify(notifyf, 1);
}

char*
maketmp(void)
{
	static char temp[] = "/tmp/mkargXXXXXX";

	mktemp(temp);
	return temp;
}

int
chgtime(char *name)
{
	Dir sbuf;

	if(access(name, AEXIST) >= 0) {
		nulldir(&sbuf);
		sbuf.mtime = time((long *)0);
		return dirwstat(name, &sbuf);
	}
	return close(create(name, OWRITE, 0666));
}

void
rcopy(char **to, Resub *match, int n)
{
	int c;
	char *p;

	*to = match->s.sp;		/* stem0 matches complete target */
	for(to++, match++; --n > 0; to++, match++){
		if(match->s.sp && match->e.ep){
			p = match->e.ep;
			c = *p;
			*p = 0;
			*to = strdup(match->s.sp);
			*p = c;
		}
		else
			*to = 0;
	}
}

ulong
mkmtime(char *name)
{
	Dir *buf;
	ulong t;

	buf = dirstat(name);
	if(buf == nil)
		return 0;
	t = buf->mtime;
	free(buf);
	return t;
}
