/*
 * Plan 9 versions of system-specific functions
 *	By convention, exported routines herein have names beginning with an
 *	upper case letter.
 */
#include "rc.h"
#include "exec.h"
#include "io.h"
#include "fns.h"
#include "getflags.h"
char *Signame[]={
	"sigexit",	"sighup",	"sigint",	"sigquit",
	"sigalrm",	"sigkill",	"sigfpe",	"sigterm",
	0
};
char *syssigname[]={
	"exit",		/* can't happen */
	"hangup",
	"interrupt",
	"quit",		/* can't happen */
	"alarm",
	"kill",
	"sys: fp: ",
	"term",
	0
};
char*
Rcmain(void)
{
	return unsharp("#9/rcmain");
}

char Fdprefix[]="/dev/fd/";
long readnb(int, char *, long);
void execfinit(void);
void execbind(void);
void execmount(void);
void execulimit(void);
void execumask(void);
void execrfork(void);
builtin Builtin[]={
	"cd",		execcd,
	"whatis",	execwhatis,
	"eval",		execeval,
	"exec",		execexec,	/* but with popword first */
	"exit",		execexit,
	"shift",	execshift,
	"wait",		execwait,
	".",		execdot,
	"finit",	execfinit,
	"flag",		execflag,
	"ulimit",	execulimit,
	"umask",	execumask,
	"rfork",	execrfork,
	0
};

void
execrfork(void)
{
	int arg;
	char *s;

	switch(count(runq->argv->words)){
	case 1:
		arg = RFENVG|RFNOTEG|RFNAMEG;
		break;
	case 2:
		arg = 0;
		for(s = runq->argv->words->next->word;*s;s++) switch(*s){
		default:
			goto Usage;
		case 'n':
			arg|=RFNAMEG;  break;
		case 'N':
			arg|=RFCNAMEG;
			break;
		case 'e':
			/* arg|=RFENVG; */  break;
		case 'E':
			arg|=RFCENVG;  break;
		case 's':
			arg|=RFNOTEG;  break;
		case 'f':
			arg|=RFFDG;    break;
		case 'F':
			arg|=RFCFDG;   break;
		}
		break;
	default:
	Usage:
		pfmt(err, "Usage: %s [nNeEsfF]\n", runq->argv->words->word);
		setstatus("rfork usage");
		poplist();
		return;
	}
	if(rfork(arg)==-1){
		pfmt(err, "rc: %s failed\n", runq->argv->words->word);
		setstatus("rfork failed");
	}
	else
		setstatus("");
	poplist();
}



#define	SEP	'\1'
char **environp;
struct word *enval(s)
register char *s;
{
	register char *t, c;
	register struct word *v;
	for(t=s;*t && *t!=SEP;t++);
	c=*t;
	*t='\0';
	v=newword(s, c=='\0'?(struct word *)0:enval(t+1));
	*t=c;
	return v;
}
void Vinit(void){
	extern char **environ;
	register char *s;
	register char **env=environ;
	environp=env;
	for(;*env;env++){
		for(s=*env;*s && *s!='(' && *s!='=';s++);
		switch(*s){
		case '\0':
		/*	pfmt(err, "rc: odd environment %q?\n", *env); */
			break;
		case '=':
			*s='\0';
			setvar(*env, enval(s+1));
			*s='=';
			break;
		case '(':	/* ignore functions for now */
			break;
		}
	}
}
char **envp;
void Xrdfn(void){
	char *p;
	register char *s;
	register int len;
	for(;*envp;envp++){
		s = *envp;
		if(strncmp(s, "fn#", 3) == 0){
			p = strchr(s, '=');
			if(p == nil)
				continue;
			*p = ' ';
			s[2] = ' ';
			len = strlen(s);
			execcmds(opencore(s, len));
			s[len] = '\0';
			return;
		}
#if 0
		for(s=*envp;*s && *s!='(' && *s!='=';s++);
		switch(*s){
		case '\0':
			pfmt(err, "environment %q?\n", *envp);
			break;
		case '=':	/* ignore variables */
			break;
		case '(':		/* Bourne again */
			s=*envp+3;
			envp++;
			len=strlen(s);
			s[len]='\n';
			execcmds(opencore(s, len+1));
			s[len]='\0';
			return;
		}
#endif
	}
	Xreturn();
}
union code rdfns[4];
void execfinit(void){
	static int first=1;
	if(first){
		rdfns[0].i=1;
		rdfns[1].f=Xrdfn;
		rdfns[2].f=Xjump;
		rdfns[3].i=1;
		first=0;
	}
	Xpopm();
	envp=environp;
	start(rdfns, 1, runq->local);
}
extern int mapfd(int);
int Waitfor(int pid, int unused0){
	thread *p;
	Waitmsg *w;
	char errbuf[ERRMAX];

	if(pid >= 0 && !havewaitpid(pid))
		return 0;
	while((w = wait()) != nil){
		delwaitpid(w->pid);
		if(w->pid==pid){
			if(strncmp(w->msg, "signal: ", 8) == 0)
				fprint(mapfd(2), "%d: %s\n", w->pid, w->msg);
			setstatus(w->msg);
			free(w);
			return 0;
		}
		if(runq->iflag && strncmp(w->msg, "signal: ", 8) == 0)
			fprint(2, "%d: %s\n", w->pid, w->msg);
		for(p=runq->ret;p;p=p->ret)
			if(p->pid==w->pid){
				p->pid=-1;
				strcpy(p->status, w->msg);
			}
		free(w);
	}

	rerrstr(errbuf, sizeof errbuf);
	if(strcmp(errbuf, "interrupted")==0) return -1;
	return 0;
}
char **mkargv(word *a)
{
	char **argv=(char **)emalloc((count(a)+2)*sizeof(char *));
	char **argp=argv+1;	/* leave one at front for runcoms */
	for(;a;a=a->next) *argp++=a->word;
	*argp=0;
	return argv;
}
/*
void addenv(var *v)
{
	char envname[256];
	word *w;
	int f;
	io *fd;
	if(v->changed){
		v->changed=0;
		snprint(envname, sizeof envname, "/env/%s", v->name);
		if((f=Creat(envname))<0)
			pfmt(err, "rc: can't open %s: %r\n", envname);
		else{
			for(w=v->val;w;w=w->next)
				write(f, w->word, strlen(w->word)+1L);
			close(f);
		}
	}
	if(v->fnchanged){
		v->fnchanged=0;
		snprint(envname, sizeof envname, "/env/fn#%s", v->name);
		if((f=Creat(envname))<0)
			pfmt(err, "rc: can't open %s: %r\n", envname);
		else{
			if(v->fn){
				fd=openfd(f);
				pfmt(fd, "fn %s %s\n", v->name, v->fn[v->pc-1].s);
				closeio(fd);
			}
			close(f);
		}
	}
}
void updenvlocal(var *v)
{
	if(v){
		updenvlocal(v->next);
		addenv(v);
	}
}
void Updenv(void){
	var *v, **h;
	for(h=gvar;h!=&gvar[NVAR];h++)
		for(v=*h;v;v=v->next)
			addenv(v);
	if(runq) updenvlocal(runq->local);
}
*/
int
cmpenv(const void *a, const void *b)
{
	return strcmp(*(char**)a, *(char**)b);
}
char **mkenv(){
	register char **env, **ep, *p, *q;
	register struct var **h, *v;
	register struct word *a;
	register int nvar=0, nchr=0, sep;
	/*
	 * Slightly kludgy loops look at locals then globals
	 */
	for(h=gvar-1;h!=&gvar[NVAR];h++) for(v=h>=gvar?*h:runq->local;v;v=v->next){
		if((v==vlook(v->name)) && v->val){
			nvar++;
			nchr+=strlen(v->name)+1;
			for(a=v->val;a;a=a->next)
				nchr+=strlen(a->word)+1;
		}
		if(v->fn){
			nvar++;
			nchr+=strlen(v->name)+strlen(v->fn[v->pc-1].s)+8;
		}
	}
	env=(char **)emalloc((nvar+1)*sizeof(char *)+nchr);
	ep=env;
	p=(char *)&env[nvar+1];
	for(h=gvar-1;h!=&gvar[NVAR];h++) for(v=h>=gvar?*h:runq->local;v;v=v->next){
		if((v==vlook(v->name)) && v->val){
			*ep++=p;
			q=v->name;
			while(*q) *p++=*q++;
			sep='=';
			for(a=v->val;a;a=a->next){
				*p++=sep;
				sep=SEP;
				q=a->word;
				while(*q) *p++=*q++;
			}
			*p++='\0';
		}
		if(v->fn){
			*ep++=p;
#if 0
			*p++='#'; *p++='('; *p++=')';	/* to fool Bourne */
			*p++='f'; *p++='n'; *p++=' ';
			q=v->name;
			while(*q) *p++=*q++;
			*p++=' ';
#endif
			*p++='f'; *p++='n'; *p++='#';
			q=v->name;
			while(*q) *p++=*q++;
			*p++='=';
			q=v->fn[v->pc-1].s;
			while(*q) *p++=*q++;
			*p++='\n';
			*p++='\0';
		}
	}
	*ep=0;
	qsort((char *)env, nvar, sizeof ep[0], cmpenv);
	return env;	
}
void Updenv(void){}
void Execute(word *args, word *path)
{
	char **argv=mkargv(args);
	char **env=mkenv();
	char file[1024];
	int nc;
	Updenv();
	for(;path;path=path->next){
		nc=strlen(path->word);
		if(nc<1024){
			strcpy(file, path->word);
			if(file[0]){
				strcat(file, "/");
				nc++;
			}
			if(nc+strlen(argv[1])<1024){
				strcat(file, argv[1]);
				execve(file, argv+1, env);
			}
			else werrstr("command name too long");
		}
	}
	rerrstr(file, sizeof file);
	pfmt(err, "%s: %s\n", argv[1], file);
	efree((char *)argv);
}
#define	NDIR	256		/* shoud be a better way */
int Globsize(char *p)
{
	ulong isglob=0, globlen=NDIR+1;
	for(;*p;p++){
		if(*p==GLOB){
			p++;
			if(*p!=GLOB) isglob++;
			globlen+=*p=='*'?NDIR:1;
		}
		else
			globlen++;
	}
	return isglob?globlen:0;
}
#define	NFD	50
#define	NDBUF	32
struct{
	Dir	*dbuf;
	int	i;
	int	n;
}dir[NFD];
int Opendir(char *name)
{
	Dir *db;
	int f;
	f=open(name, 0);
	if(f==-1)
		return f;
	db = dirfstat(f);
	if(db!=nil && (db->mode&DMDIR)){
		if(f<NFD){
			dir[f].i=0;
			dir[f].n=0;
		}
		free(db);
		return f;
	}
	free(db);
	close(f);
	return -1;
}
int Readdir(int f, char *p, int onlydirs)
{
	int n;
	USED(onlydirs);	/* only advisory */

	if(f<0 || f>=NFD)
		return 0;
	if(dir[f].i==dir[f].n){	/* read */
		free(dir[f].dbuf);
		dir[f].dbuf=0;
		n=dirread(f, &dir[f].dbuf);
		if(n>=0)
			dir[f].n=n;
		else
			dir[f].n=0;
		dir[f].i=0;
	}
	if(dir[f].i==dir[f].n)
		return 0;
	strcpy(p, dir[f].dbuf[dir[f].i].name);
	dir[f].i++;
	return 1;
}
void Closedir(int f){
	if(f>=0 && f<NFD){
		free(dir[f].dbuf);
		dir[f].i=0;
		dir[f].n=0;
		dir[f].dbuf=0;
	}
	close(f);
}
int interrupted = 0;
void
notifyf(void *unused0, char *s)
{
	int i;
	for(i=0;syssigname[i];i++)
		if(strncmp(s, syssigname[i], strlen(syssigname[i]))==0){
			if(strncmp(s, "sys: ", 5)!=0){
				if(kidpid && !interrupted){
					interrupted=1;
					postnote(PNGROUP, kidpid, s);
				}
				interrupted = 1;
			}
			goto Out;
		}
	if(strcmp(s, "sys: window size change") != 0)
	if(strcmp(s, "sys: write on closed pipe") != 0)
	if(strcmp(s, "sys: child") != 0)
		pfmt(err, "rc: note: %s\n", s);
	noted(NDFLT);
	return;
Out:
	if(strcmp(s, "interrupt")!=0 || trap[i]==0){
		trap[i]++;
		ntrap++;
	}
	if(ntrap>=32){	/* rc is probably in a trap loop */
		pfmt(err, "rc: Too many traps (trap %s), aborting\n", s);
		abort();
	}
	noted(NCONT);
}
void Trapinit(void){
	notify(notifyf);
}
void Unlink(char *name)
{
	remove(name);
}
long Write(int fd, char *buf, long cnt)
{
	return write(fd, buf, (long)cnt);
}
long Read(int fd, char *buf, long cnt)
{
	int i;

	i = readnb(fd, buf, cnt);
	if(ntrap) dotrap();
	return i;
}
long Seek(int fd, long cnt, long whence)
{
	return seek(fd, cnt, whence);
}
int Executable(char *file)
{
	Dir *statbuf;
	int ret;

	statbuf = dirstat(file);
	if(statbuf == nil) return 0;
	ret = ((statbuf->mode&0111)!=0 && (statbuf->mode&DMDIR)==0);
	free(statbuf);
	return ret;
}
int Creat(char *file)
{
	return create(file, 1, 0666L);
}
int Dup(int a, int b){
	return dup(a, b);
}
int Dup1(int a){
	return dup(a, -1);
}
void Exit(char *stat)
{
	Updenv();
	setstatus(stat);
	exits(truestatus()?"":getstatus());
}
int Eintr(void){
	return interrupted;
}
void Noerror(void){
	interrupted=0;
}
int
Isatty(int fd){
	return isatty(fd);
}
void Abort(void){
	pfmt(err, "aborting\n");
	flush(err);
	Exit("aborting");
}
void Memcpy(char *a, char *b, long n)
{
	memmove(a, b, (long)n);
}
void *Malloc(ulong n){
	return malloc(n);
}

int
exitcode(char *msg)
{
	int n;
	
	n = atoi(msg);
	if(n == 0)
		n = 1;
	return n;
}

int *waitpids;
int nwaitpids;

void
addwaitpid(int pid)
{
	waitpids = realloc(waitpids, (nwaitpids+1)*sizeof waitpids[0]);
	if(waitpids == 0)
		panic("Can't realloc %d waitpids", nwaitpids+1);
	waitpids[nwaitpids++] = pid;
}

void
delwaitpid(int pid)
{
	int r, w;
	
	for(r=w=0; r<nwaitpids; r++)
		if(waitpids[r] != pid)
			waitpids[w++] = waitpids[r];
	nwaitpids = w;
}

void
clearwaitpids(void)
{
	nwaitpids = 0;
}

int
havewaitpid(int pid)
{
	int i;
	
	for(i=0; i<nwaitpids; i++)
		if(waitpids[i] == pid)
			return 1;
	return 0;
}