#include "rc.h"
#include "y.tab.h"

typedef struct Kw	Kw;

#define	NKW	30
#define NVAR	521

struct Kw{
	char	*name;
	int	type;
	Kw	*next;
};

void	updenvlocal(Var *v);
void	addenv(Var *v);

Kw	*kw[NKW];
Var	*gvar[NVAR];

int
hash(char *s, int n)
{
	int h=0, i=1;

	while(*s)
		h+=*s++*i++;
	h%=n;
	return h<0?h+n:h;
}

void
kenter(int type, char *name)
{
	int h=hash(name, NKW);
	Kw *p=new(Kw);
	p->type=type;
	p->name=name;
	p->next=kw[h];
	kw[h]=p;
}

void
vinit(void)
{
	char *buf, **env, envname[256], *name, *p, *s;
	Dir *ent;
	int i, fd, dir, len, n, nent;
	Word *w, *val;
	int f;

	dir = open("/env", 0);
	if(dir < 0){
		pfmt(err, "rc: can't open /env: %r\n");
		return;
	}
	ent = nil;
	for(;;){
		nent = dirread(dir, &ent);
		if(nent <= 0)
			break;
		for(i=0; i<nent; i++){
			len = ent[i].length;
			if(len && strncmp(ent[i].name, "fn#", 3)!=0){
				snprint(envname, sizeof envname, "/env/%s", ent[i].name);
				if((f=open(envname, 0))>=0){
					buf=malloc((int)len+1);
					read(f, buf, (long)len);
					val=0;
					/* Charitably add a 0 at the end if need be */
					if(buf[len-1]) buf[len++]='\0';
					s=buf+len-1;
					for(;;){
						while(s!=buf && s[-1]!='\0') --s;
						val=newword(s, val);
						if(s==buf) break;
						--s;
					}
					setvar(ent[i].name, val);
					vlook(ent[i].name)->changed=0;
					close(f);
					free(buf);
				}
			}
		}
		free(ent);
	}
	close(dir);
}


Tree *
klook(char *name)
{
	Kw *p;
	Tree *t=token(name, WORD);
	for(p=kw[hash(name, NKW)];p;p=p->next) {
		if(strcmp(p->name, name)==0){
			t->type=p->type;
			t->iskw=1;
			break;
		}
	}
	return t;
}

Var *
gvlook(char *name)
{
	int h=hash(name, NVAR);
	Var *v;
	for(v=gvar[h]; v; v=v->next)
		if(strcmp(v->name, name)==0)
			return v;

	return gvar[h]=newvar(strdup(name), gvar[h]);
}

Var *
vlook(char *name)
{
	Var *v;
	if(runq)
		for(v=runq->local; v; v=v->next)
			if(strcmp(v->name, name)==0)
				return v;
	return gvlook(name);
}

void
setvar(char *name, Word *val)
{
	Var *v=vlook(name);
	freewords(v->val);
	v->val=val;
	v->changed=1;
}

Var *
newvar(char *name, Var *next)
{
	Var *v=new(Var);
	v->name=name;
	v->val=0;
	v->fn=0;
	v->changed=0;
	v->fnchanged=0;
	v->next=next;
	return v;
}


void
execfinit(void)
{
}

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);
}

void
addenv(Var *v)
{
	char buf[100], *p;
	Io *f;
	Word *w;
	int i, n;

	if(v->changed){
		v->changed=0;
		p = 0;
		n = 0;
		if(v->val) {
			for(w=v->val; w; w=w->next) {
				i = strlen(w->word);
				p = realloc(p, n+i+1);
				memmove(p+n, w->word, i);
				n+=i;
				p[n++] = IWS;
			}
			p[n-1] = 0;
			putenv(v->name, p);
		} else
			putenv(v->name, "");
		free(p);
	}

	if(v->fnchanged){
		v->fnchanged=0;
		snprint(buf, sizeof(buf), "fn#%s", v->name);
		f = openstr();
		pfmt(f, "fn %s %s\n", v->name, v->fn[v->pc-1].s);
		putenv(buf, f->strp);
		closeio(f);
	}
}

void
updenvlocal(Var *v)
{
	if(v){
		updenvlocal(v->next);
		addenv(v);
	}
}
