/*
 *
 * params.c - parameters
 *
 * This file is part of zsh, the Z shell.
 *
 * This software is Copyright 1992 by Paul Falstad
 *
 * Permission is hereby granted to copy, reproduce, redistribute or otherwise
 * use this software as long as: there is no monetary profit gained
 * specifically from the use or reproduction of this software, it is not
 * sold, rented, traded or otherwise marketed, and this copyright notice is
 * included prominently in any copy made. 
 *
 * The author make no claims as to the fitness or correctness of this software
 * for any use whatsoever, and it is provided as is. Any use of this software
 * is at the user's own risk. 
 *
 */

#include "zsh.h"
#include "version.h"
#include <pwd.h>

#define new(X) (X=(vptr)alloc(sizeof(*(X))))

static Param argvparam;

struct iparam {
	struct hashnode *next; int canfree; char *nam; /* hash data */
	vptr value;
	int (*func1)(); /* set func */
	int (*func2)(); /* get func */
	int ct;				/* output base or field width */
	int flags;
	vptr data;			/* used by getfns */
	char *env;			/* location in environment, if exported */
	char *ename;		/* name of corresponding environment var */
	};

#define IFN(X) ((int (*)())(X))

/* put predefined params in hash table */

void setupparams() /**/
{
static struct iparam pinit[] = {
#define IPDEF1(A,B,C,D) {NULL,0,A,NULL,IFN(C),IFN(B),10,\
		PMFLAG_i|PMFLAG_SPECIAL|D,NULL,NULL,NULL}
	IPDEF1("#",poundgetfn,IFN(nullsetfn),PMFLAG_r),
	IPDEF1("ARGC",poundgetfn,IFN(nullsetfn),PMFLAG_r),
	IPDEF1("ERRNO",errnogetfn,IFN(nullsetfn),PMFLAG_r),
	IPDEF1("GID",gidgetfn,IFN(nullsetfn),PMFLAG_r),
	IPDEF1("HISTSIZE",histsizegetfn,histsizesetfn,0),
	IPDEF1("LITHISTSIZE",lithistsizegetfn,lithistsizesetfn,0),
	IPDEF1("RANDOM",randomgetfn,randomsetfn,0),
	IPDEF1("SECONDS",secondsgetfn,secondssetfn,0),
	IPDEF1("UID",uidgetfn,IFN(nullsetfn),PMFLAG_r),

#define IPDEF2(A,B,C,D) {NULL,0,A,NULL,IFN(C),IFN(B),0,\
		PMFLAG_SPECIAL|D,NULL,NULL,NULL}
	IPDEF2("-",dashgetfn,IFN(nullsetfn),PMFLAG_r),
	IPDEF2("HISTCHARS",histcharsgetfn,histcharssetfn,0),
	IPDEF2("HOME",homegetfn,homesetfn,0),
	IPDEF2("TERM",termgetfn,termsetfn,0),
	IPDEF2("WORDCHARS",wordcharsgetfn,wordcharssetfn,0),
	IPDEF2("IFS",ifsgetfn,ifssetfn,0),
	IPDEF2("_",underscoregetfn,IFN(nullsetfn),PMFLAG_r),

#define IPDEF3(A) {NULL,0,A,NULL,IFN(nullsetfn),IFN(strconstgetfn),0,PMFLAG_r|\
		PMFLAG_SPECIAL,NULL,NULL,NULL}
	IPDEF3("HOSTTYPE"),
	IPDEF3("VERSION"),

#define IPDEF4(A,B) {NULL,0,A,NULL,IFN(nullsetfn),IFN(intvargetfn),10,\
		PMFLAG_r|PMFLAG_i|PMFLAG_SPECIAL,(vptr)B,NULL,NULL}
	IPDEF4("!",&lastpid),
	IPDEF4("$",&mypid),
	IPDEF4("?",&lastval),
	IPDEF4("status",&lastval),
	IPDEF4("LINENO",&lineno),
	IPDEF4("PPID",&ppid),

#define IPDEF5(A,B) {NULL,0,A,NULL,IFN(intvarsetfn),IFN(intvargetfn),10,\
		PMFLAG_i|PMFLAG_SPECIAL,(vptr)B,NULL,NULL}
	IPDEF5("BAUD",&baud),
	IPDEF5("COLUMNS",&columns),
	IPDEF5("DIRSTACKSIZE",&dirstacksize),
	IPDEF5("LINES",&lines),
	IPDEF5("LISTMAX",&listmax),
	IPDEF5("LOGCHECK",&logcheck),
	IPDEF5("MAILCHECK",&mailcheck),
	IPDEF5("OPTIND",&zoptind),
	IPDEF5("PERIOD",&period),
	IPDEF5("REPORTTIME",&reporttime),
	IPDEF5("SAVEHIST",&savehist),
	IPDEF5("SHLVL",&shlvl),
	IPDEF5("TMOUT",&tmout),

#define IPDEF6(A,B) {NULL,0,A,NULL,IFN(nullsetfn),IFN(strvargetfn),0,\
		PMFLAG_r|PMFLAG_SPECIAL,(vptr)B,NULL,NULL}
	IPDEF6("LOGNAME",&logname),
	IPDEF6("PWD",&pwd),
	IPDEF6("TTY",&ttystrname),
	IPDEF6("USERNAME",&username),

#define IPDEF7(A,B) {NULL,0,A,NULL,IFN(strvarsetfn),IFN(strvargetfn),0,\
		PMFLAG_SPECIAL,(vptr)B,NULL,NULL}
	IPDEF7("FCEDIT",&fceditparam),
	IPDEF7("HOST",&hostnam),
	IPDEF7("OLDPWD",&oldpwd),
	IPDEF7("OPTARG",&optarg),
	IPDEF7("MAIL",&mailfile),
	IPDEF7("NULLCMD",&nullcmd),
	IPDEF7("POSTEDIT",&postedit),
	IPDEF7("prompt",&prompt),
	IPDEF7("PROMPT",&prompt),
	IPDEF7("PROMPT2",&prompt2),
	IPDEF7("PROMPT3",&prompt3),
	IPDEF7("PROMPT4",&prompt4),
	IPDEF7("READNULLCMD",&readnullcmd),
	IPDEF7("RPROMPT",&rprompt),
	IPDEF7("PS1",&prompt),
	IPDEF7("PS2",&prompt2),
	IPDEF7("PS3",&prompt3),
	IPDEF7("PS4",&prompt4),
	IPDEF7("RPS1",&rprompt),
	IPDEF7("SPROMPT",&sprompt),
	IPDEF7("TIMEFMT",&timefmt),
	IPDEF7("TMPPREFIX",&tmpprefix),
	IPDEF7("WATCHFMT",&watchfmt),
	IPDEF7("0",&argzero),

#define IPDEF8(A,B,C) {NULL,0,A,NULL,IFN(colonarrsetfn),IFN(colonarrgetfn),0,\
		PMFLAG_SPECIAL,(vptr)C,NULL,B}
	IPDEF8("CDPATH","cdpath",&cdpath),
	IPDEF8("FIGNORE","fignore",&fignore),
	IPDEF8("FPATH","fpath",&fpath),
	IPDEF8("MAILPATH","mailpath",&mailpath),
	IPDEF8("MANPATH","manpath",&manpath),
	IPDEF8("WATCH","watch",&watch),
	IPDEF8("HOSTS","hosts",&hosts),
	IPDEF8("PSVAR","psvar",&psvar),
	IPDEF8("PATH",NULL,NULL),

#define IPDEF9(A,B,C,D) {NULL,0,A,NULL,IFN(arrvarsetfn),IFN(arrvargetfn),0,\
		PMFLAG_A|PMFLAG_SPECIAL|C,(vptr)B,NULL,D}
	IPDEF9("cdpath",&cdpath,0,"CDPATH"),
	IPDEF9("fignore",&fignore,0,"FIGNORE"),
	IPDEF9("fpath",&fpath,0,"FPATH"),
	IPDEF9("mailpath",&mailpath,0,"MAILPATH"),
	IPDEF9("manpath",&manpath,0,"MANPATH"),
	IPDEF9("watch",&watch,0,"WATCH"),
	IPDEF9("hosts",&hosts,0,"HOSTS"),
	IPDEF9("psvar",&psvar,0,"PSVAR"),
	IPDEF9("signals",&sigptr,PMFLAG_r,NULL),
	IPDEF9("argv",&pparams,0,NULL),
	IPDEF9("*",&pparams,0,NULL),
	IPDEF9("@",&pparams,0,NULL),

#define IPDEF10(A,C,D) {NULL,0,A,NULL,IFN(D),IFN(C),0,\
		PMFLAG_A|PMFLAG_SPECIAL,NULL,NULL,NULL}
	IPDEF10("path",pathgetfn,pathsetfn),
	IPDEF10("hostcmds",nullgetfn,hostcmdssetfn),
	IPDEF10("optcmds",nullgetfn,optcmdssetfn),
	IPDEF10("bindcmds",nullgetfn,bindcmdssetfn),
	IPDEF10("varcmds",nullgetfn,varcmdssetfn),
	{NULL,}
	};
struct iparam *ip;

	for (ip = pinit; ip->nam; ip++) addhperm(ip->nam,ip,paramtab,(FFunc) 0);
	argvparam = gethnode("argv",paramtab);

	((struct iparam *)gethnode("HOSTTYPE", paramtab))->data = ztrdup(HOSTTYPE);
	((struct iparam *)gethnode("VERSION", paramtab))->data = ztrdup(VERSIONSTR);
}

static int unsetflag;

struct param *createparam(name,value,flags) /**/
char *name;vptr value;int flags;
{
struct param *pm;
char buf[20];

	pm = zcalloc(sizeof *pm);
	if (isset(ALLEXPORT))
		flags |= PMFLAG_x;
	pm->flags = flags;
	if ((flags & PMTYPE) == PMFLAG_s) {
		pm->u.str = value;
		pm->sets.cfn = strsetfn;
		pm->gets.cfn = strgetfn;
	} else if ((flags & PMTYPE) == PMFLAG_A) {
		pm->u.arr = value;
		pm->sets.afn = arrsetfn;
		pm->gets.afn = arrgetfn;
	} else {
		pm->u.val = (value) ? matheval(value) : 0;
		pm->sets.ifn = intsetfn;
		pm->gets.ifn = intgetfn;
		sprintf(buf,"%ld",pm->u.val);
		value = buf;
	}
	if (flags & PMFLAG_x)
		pm->env = addenv(name,value);
	addhnode(ztrdup(name),pm,paramtab,freepm);
	return pm;
}

int isident(s) /**/
char *s;
{
char *ss;

	for (ss = s; *ss; ss++) if (!iident(*ss)) break;
	if (!*ss) return 1;
	if (*ss != '[') return 0;
	(void) mathevalarg(++ss, &ss);
	if(*ss == ',' || *ss == Comma)
		(void) mathevalarg(++ss,&ss);
	if(*ss != ']' || ss[1]) return 0;
	return 1;
}

Value getvalue(pptr,bracks) /**/
char **pptr;int bracks;
{
char *s = *pptr,*t = *pptr;
char sav;
Value v;

	if (idigit(*s)) while (idigit(*s)) s++;
	else if (iident(*s)) while (iident(*s)) s++;
	else if (*s == Quest) *s++ = '?';
	else if (*s == Pound) *s++ = '#';
	else if (*s == String) *s++ = '$';
	else if (*s == Qstring) *s++ = '$';
	else if (*s == Star) *s++ = '*';
	else if (*s == '#' || *s == '-' || *s == '?' || *s == '$' ||
				*s == '_' || *s == '!' || *s == '@' || *s == '*') s++;
	else return NULL;
	if (sav = *s) *s = '\0';
	if (idigit(*t) && *t != '0') {
		v = (Value) hcalloc(sizeof *v);
		v->pm = argvparam;
		v->a = v->b = atoi(t)-1;
		if (sav)
			*s = sav;
	} else {
		struct param *pm;
		int isvarat = !strcmp(t, "@");

		pm = gethnode(t,paramtab);
		if (sav)
			*s = sav;
		*pptr = s;
		if (!pm)
			return NULL;
		v = hcalloc(sizeof *v);
		if (pmtype(pm) == PMFLAG_A)
			v->isarr = isvarat ? -1 : 1;
		v->pm = pm;
		v->a = 0; v->b = -1;
		if (bracks && (*s == '[' || *s == Inbrack)) {
			int a,b;
			char *olds = s,*t;

			*s++ = '[';
			for (t = s; *t && *t != ']' && *t != Outbrack; t++)
				if (itok(*t))
					*t = ztokens[*t-Pound];
			if (*t == Outbrack)
				*t = ']';
			if ((s[0] == '*' || s[0] == '@')  && s[1] == ']') {
				if (v->isarr) v->isarr = (s[0] == '*') ? 1 : -1;
				v->a = 0;
				v->b = -1;
				s += 2;
			} else {
				a = mathevalarg(s,&s);
				if (a > 0) a--;
				if (*s == ',' || *s == Comma) {
					s++;
					b = mathevalarg(s,&s);
					if (b > 0) b--;
				} else
					b = a;
				if (*s == ']') {
					s++;
					if (v->isarr && a == b)
						v->isarr = 0;
					v->a = a;
					v->b = b;
				} else
					s = olds;
			}
		}
	}
	if (!bracks && *s)
		return NULL;
	*pptr = s;
	return v;
}

char *getstrvalue(v) /**/
Value v;
{
char *s,**ss;
static char buf[20];

	if (!v)
		return "";
	if (pmtype(v->pm) != PMFLAG_A) {
		if ((pmtype(v->pm) == PMFLAG_i))
			convbase(s = buf,v->pm->gets.ifn(v->pm),v->pm->ct);
		else
			s = v->pm->gets.cfn(v->pm);
		if (v->a == 0 && v->b == -1) return s;
		if (v->a < 0) v->a += strlen(s);
		if (v->b < 0) v->b += strlen(s);
		s = (v->a > strlen(s)) ? strdup("") : strdup(s+v->a);
		if (v->b < v->a) s[0] = '\0';
		else if (v->b-v->a < strlen(s)) s[v->b-v->a+1] = '\0';
		return s;
	}
	if (v->isarr) return spacejoin(v->pm->gets.afn(v->pm));

	ss = v->pm->gets.afn(v->pm);
	if (v->a < 0) v->a += arrlen(ss);
	s = (v->a >= arrlen(ss) || v->a < 0) ? "" : ss[v->a];
	return s;
}

char **getarrvalue(v) /**/
Value v;
{
char **s;
static char *nular[] = { "", NULL };

	if (!v)
		return arrdup(nular);
	s = v->pm->gets.afn(v->pm);
	if (v->a == 0 && v->b == -1) return s;
	if (v->a < 0) v->a += arrlen(s);
	if (v->b < 0) v->b += arrlen(s);
	if (v->a > arrlen(s) || v->a < 0)
		s = arrdup(nular);
	else
		s = arrdup(s)+v->a;
	if (v->b < v->a) s[0] = NULL;
	else if (v->b-v->a < arrlen(s)) s[v->b-v->a+1] = NULL;
	return s;
}

long getintvalue(v) /**/
Value v;
{
char **ss;

	if (!v || v->isarr)
		return 0;
	if (pmtype(v->pm) != PMFLAG_A) {
		if (pmtype(v->pm) == PMFLAG_i)
			return v->pm->gets.ifn(v->pm);
		return atol(v->pm->gets.cfn(v->pm));
	}
	ss = v->pm->gets.afn(v->pm);
	if (v->a < 0) v->a += arrlen(ss);
	if (v->a < 0 || v->a >= arrlen(ss)) return 0;
	return atol(ss[v->a]);
}

void setstrvalue(v,val) /**/
Value v;char *val;
{
	if (v->pm->flags & PMFLAG_r) {
		free(val);
		return;
	}
	if (v->pm->env)
		v->pm->env = replenv(v->pm->env,val);
	else if (isset(ALLEXPORT)) {
		v->pm->flags |= PMFLAG_x;
		v->pm->env = addenv(v->pm->nam,val);
	}
	switch (pmtype(v->pm)) {
		case PMFLAG_s:
			if (v->a == 0 && v->b == -1)
				(v->pm->sets.cfn)(v->pm,val);
			else {
				char *z,*y,*x;

				z = strdup((v->pm->gets.cfn)(v->pm));
				if (v->a < 0) {
					v->a += strlen(z);
					if (v->a < 0) v->a = 0;
				}
				if (v->a > strlen(z)) v->a = strlen(z);
				if (v->b < 0) v->b += strlen(z);
				if (v->b < v->a) v->b = v->a;
				z[v->a] = '\0';
				y = z+v->b+1;
				x = zalloc(strlen(z)+strlen(y)+strlen(val)+1);
				strcpy(x,z);
				strcat(x,val);
				strcat(x,y);
				(v->pm->sets.cfn)(v->pm,x);
			}
			if (v->pm->flags & (PMFLAG_L|PMFLAG_R|PMFLAG_Z) && !v->pm->ct)
				v->pm->ct = strlen(val);
			break;
		case PMFLAG_i:
			(v->pm->sets.ifn)(v->pm,matheval(val));
			if (!v->pm->ct && lastbase != 1)
				v->pm->ct = lastbase;
			free(val);
			break;
		case PMFLAG_A:
			if (v->a != v->b)
				zerr("illegal array assignment",NULL,0);
			else {
				char **ss = (v->pm->gets.afn)(v->pm);
				int ac,ad,t0;

				ac = arrlen(ss);
				if (v->a < 0) {
					v->a += ac;
					if (v->a < 0) v->a = 0;
				}
				if (v->a >= ac) {
					char **st = ss;

					ad = v->a+1;
					ss = zalloc((ad+1)*sizeof *ss);
					for (t0 = 0; t0 != ac; t0++)
						ss[t0] = ztrdup(st[t0]);
					while (ac < ad)
						ss[ac++] = ztrdup("");
					ss[ac] = NULL;
				}
				if (ss[v->a]) free(ss[v->a]);
				ss[v->a] = val;
				(v->pm->sets.afn)(v->pm,ss);
			}
			break;
	}
}

void setintvalue(v,val) /**/
Value v;long val;
{
char buf[20];

	if (v->pm->flags & PMFLAG_r)
		return;
	sprintf(buf,"%ld",val);
	if (v->pm->env) {
		v->pm->env = replenv(v->pm->env,buf);
	}
	else if (isset(ALLEXPORT)) {
		v->pm->flags |= PMFLAG_x;
		v->pm->env = addenv(v->pm->nam,buf);
	}
	switch (pmtype(v->pm))
		{
		case PMFLAG_s:
			(v->pm->sets.cfn)(v->pm,ztrdup(buf));
			break;
		case PMFLAG_i:
			(v->pm->sets.ifn)(v->pm,val);
			if (!v->pm->ct && lastbase != -1)
				v->pm->ct = lastbase;
			break;
		case PMFLAG_A:
			zerr("attempt to assign integer to array",NULL,0);
			break;
		}
}

void setintenv(s,val) /**/
char *s; long val;
{
Param pm;
char buf[20];

	if ((pm = gethnode(s,paramtab)) && pm->env) {
		sprintf(buf,"%ld",val);
		pm->env = replenv(pm->env,buf);
	}
}

void setarrvalue(v,val) /**/
Value v;char **val;
{
	if (v->pm->flags & PMFLAG_r) {
		freearray(val);
		return;
	}
	if (pmtype(v->pm) != PMFLAG_A)
		{
		freearray(val);
		zerr("attempt to assign array value to non-array",NULL,0);
		return;
		}
	if (v->a == 0 && v->b == -1)
		(v->pm->sets.afn)(v->pm,val);
	else {
		char **ss = (v->pm->gets.afn)(v->pm);
		int ac,ad,t0;

		ac = arrlen(ss);
		if (v->a < 0) {
			v->a += ac;
			if (v->a < 0) v->a = 0;
		}
		if (v->b < 0) v->b += ac;
		if (v->b < v->a) v->b = v->a;
		t0 = arrlen(val) - (v->b - v->a + 1);
		if (v->b >= ac || t0 != 0) {
			char **st = ss;

			ad = (v->b > (ac - 1) ? v->b : (ac - 1)) + t0 + 1;
			ss = zalloc((ad+1)*sizeof *ss);
			for (t0 = 0; t0 < v->a; t0++)
				ss[t0] = ztrdup(t0 < ac ? st[t0] : "");
			while (*val) ss[t0++] = *val++;
			while (++v->b < ac) ss[t0++] = ztrdup(st[v->b]);
			ss[t0] = NULL;
		} else {
			for (t0 = v->a; t0 <= v->b; t0++) {
				if (ss[t0]) free(ss[t0]);
				ss[t0] = val[t0];
			}
		}
		(v->pm->sets.afn)(v->pm,ss);
	}
}

int getparamtype(s,l) /**/
char *s;int l;
{
char sav,*t = s;
Value v;

	sav = t[l];
	t[l] = '\0';

	if (!(v = getvalue(&s,0)))
		return -1;

	t[l] = sav;
	return (pmtype(v->pm));
}

char *getsparamval(s,l) /**/
char *s;int l;
{
char sav,*t = s;
Value v;

	sav = t[l];
	t[l] = '\0';
	
	if (!(v = getvalue(&s,0)))
		return NULL;
	t[l] = sav;
	t = getstrvalue(v);
	return t;
}

long getiparam(s) /**/
char *s;
{
Value v;

	if (!(v = getvalue(&s,0)))
		return 0;
	return getintvalue(v);
}

char *getsparam(s) /**/
char *s;
{
Value v;

	if (!(v = getvalue(&s,0)))
		return NULL;
	return getstrvalue(v);
}

char **getaparam(s) /**/
char *s;
{
Value v;

	if (!((v = getvalue(&s,0)) && v->isarr))
		return NULL;
	return getarrvalue(v);
}

Param setsparam(s,val) /**/
char *s;char *val;
{
Value v;
char *t = s;
char *ss;

	if (!isident(s)) {
		zerr("not an identifier: %s",s,0);
		free(val);
		return NULL;
	}
	if (ss = strchr(s, '[')) {
		*ss = '\0';
		if (!(v = getvalue(&s,1)))
			createparam(t,zcalloc(sizeof val),PMFLAG_A);
		*ss = '[';
		v = getvalue(&t,1);
	} else {
		if (!(v = getvalue(&s,1)))
			return createparam(t,val,PMFLAG_s);
		if ((v->pm->flags & PMTYPE) != PMFLAG_s &&
				!(v->pm->flags & PMFLAG_SPECIAL)) {
			unsetparam(t);
			return createparam(t,val,PMFLAG_s);
		}
	}
	setstrvalue(v,val);
	return v->pm;
}

Param setaparam(s,val) /**/
char *s;char **val;
{
Value v;
char *t = s;
char *ss;

	if (!isident(s))
		{
		zerr("not an identifier: %s",s,0);
		freearray(val);
		return NULL;
		}
	if(ss = strchr(s, '[')) {
		*ss = '\0';
		if (!(v = getvalue(&s,1)))
			createparam(t,zcalloc(sizeof val),PMFLAG_A);
		*ss = '[';
		v = getvalue(&t,1);
	} else {
		if (!(v = getvalue(&s,1)))
			return createparam(t,val,PMFLAG_A);
		if ((v->pm->flags & PMTYPE) != PMFLAG_A &&
				!(v->pm->flags & PMFLAG_SPECIAL)) {
			unsetparam(t);
			return createparam(t,val,PMFLAG_A);
		}
	}
	setarrvalue(v,val);
	return v->pm;
}

Param setiparam(s,val) /**/
char *s;long val;
{
Value v;
char *t = s;
Param pm;

	if (!isident(s))
		{
		zerr("not an identifier: %s",s,0);
		return NULL;
		}
	if (!(v = getvalue(&s,0)))
		{
		pm = createparam(t,NULL,PMFLAG_i);
		pm->u.val = val;
		return pm;
		}
	setintvalue(v,val);
	return v->pm;
}

void unsetparam(s) /**/
char *s;
{
Param pm;

	if (!(pm = gethnode(s,paramtab)))
		return;
	if (pm->flags & PMFLAG_r)
		return;
	unsetflag = 1;
	switch (pmtype(pm))
		{
		case 0:
			(pm->sets.cfn)(pm,ztrdup(""));
			break;
		case PMFLAG_i:
			(pm->sets.ifn)(pm,0);
			break;
		case PMFLAG_A:
			(pm->sets.afn)(pm,mkarray(NULL));
			break;
		}
	if (pmtype(pm) == PMFLAG_s && (pm->flags & PMFLAG_x)) {
		delenv(pm->env);
		free(pm->env);
		if (pm->flags & PMFLAG_SPECIAL)
			pm->env = NULL;
	}
	if (!(pm->flags & PMFLAG_SPECIAL))
		freepm(remhnode(s,paramtab));
	unsetflag = 0;
}

void intsetfn(pm,x) /**/
Param pm;long x;
{
	pm->u.val = x;
}

long intgetfn(pm) /**/
Param pm;
{
	return pm->u.val;
}

void strsetfn(pm,x) /**/
Param pm;char *x;
{
	if (x) 
		{
		if (pm->u.str)
			free(pm->u.str);
		pm->u.str = x;
		}
}

char *strgetfn(pm) /**/
Param pm;
{
	return pm->u.str;
}

void nullsetfn(pm,x) /**/
Param pm; char *x;
{
	free(x);
}

void arrsetfn(pm,x) /**/
Param pm;char **x;
{
int ct;

	if (x)
		{
		if (pm->u.arr && pm->u.arr != x)
			freearray(pm->u.arr);
		pm->u.arr = x;
		for (ct = 0; *x; x++,ct++);
		pm->ct = ct;
		}
}

char **arrgetfn(pm) /**/
Param pm;
{
	return pm->u.arr;
}

void intvarsetfn(pm,x) /**/
Param pm;long x;
{
	*((long *) pm->data) = x;
}

long intvargetfn(pm) /**/
Param pm;
{
	return *((long *) pm->data);
}

void strvarsetfn(pm,x) /**/
Param pm;char *x;
{
char **q = ((char **) pm->data);

	if (*q) free(*q);
	*q = x;
}

void strvarnonullsetfn(pm,x) /**/
Param pm;char *x;
{
char **q = ((char **) pm->data);

	if (*q) free(*q);
	*q = (x) ? x : ztrdup("");
}

char *strvargetfn(pm) /**/
Param pm;
{
char *s;

	s = *((char **) pm->data);
	if (!s) return "";
	return s;
}

char *strconstgetfn(pm) /**/
Param pm;
{
	return (char *) pm->data;
}

void colonarrsetfn(pm,x) /**/
Param pm;char *x;
{
char **s,**t,*u,*up;

	s = colonsplit(x);
	free(x);
	if (pm->data != &fignore)
		for (t = s; *t; t++) {
			u = *t;
			if (*u == '~') *u = Tilde;
			if (*u == '=') *u = Equals;
			up = hcalloc(strlen(u)+1);
			strcpy(up,u);
			u = up;
			filesub(&u);
			if (!*u) u = ".";
			free(*t);
			*t = ztrdup(u);
		}
	if (pm->data) {
		freearray(*((char ***) pm->data));
		*((char ***) pm->data) = s;
		if (pm->ename)
			arrfixenv(pm->ename,s);
	} else {
		freearray(path);
		path = s;
		newcmdnamtab();
		arrfixenv("PATH",s);
	}
}

char *colonarrgetfn(pm) /**/
Param pm;
{
	if ((char **) pm->data)
		return colonjoin(*(char ***) pm->data);
	else
		return colonjoin(path);
}

char **arrvargetfn(pm) /**/
Param pm;
{
	return *((char ***) pm->data);
}

void arrvarsetfn(pm,x) /**/
Param pm;char **x;
{
	if ((*(char ***) pm->data) != x)
		freearray(*(char ***) pm->data);
	*((char ***) pm->data) = x;
	if (pm->ename)
		arrfixenv(pm->ename,x);
}

char **pathgetfn(pm) /**/
Param pm;
{
	return path;
}

void pathsetfn(pm,x) /**/
Param pm;char **x;
{
	if (path != x) freearray(path);
	path = x;
	newcmdnamtab();
	arrfixenv("PATH",x);
}

void hostcmdssetfn(pm,x) /**/
Param pm;char **x;
{
	compctl_process(x,CC_HOSTS,NULL);
	freearray(x);
}

void optcmdssetfn(pm,x) /**/
Param pm;char **x;
{
	compctl_process(x,CC_OPTIONS,NULL);
	freearray(x);
}

void bindcmdssetfn(pm,x) /**/
Param pm;char **x;
{
	compctl_process(x,CC_BINDINGS,NULL);
	freearray(x);
}

void varcmdssetfn(pm,x) /**/
Param pm;char **x;
{
	compctl_process(x,CC_VARS,NULL);
	freearray(x);
}

char **nullgetfn(pm) /**/
Param pm;
{
static char *nl = NULL; return &nl;
}

void unsettablesetfn(pm,x) /**/
Param pm;char *x;
{ ; }

long poundgetfn(pm) /**/
Param pm;
{
	return arrlen(pparams);
}

long randomgetfn(pm) /**/
Param pm;
{
	return rand() & 0x7fff;
}

void randomsetfn(pm,v) /**/
Param pm;long v;
{
	srand((unsigned int) v);
}

long secondsgetfn(pm) /**/
Param pm;
{
	return time(NULL)-shtimer;
}

void secondssetfn(pm,x) /**/
Param pm;long x;
{
	shtimer = time(NULL)-x;
}

long uidgetfn(pm) /**/
Param pm;
{
	return getuid();
}

long gidgetfn(pm) /**/
Param pm;
{
	return getegid();
}

char *usernamegetfn(pm) /**/
Param pm;
{
struct passwd *pwd;

	pwd = getpwuid(getuid());
	return pwd->pw_name;
}

char *hostgetfn(pm) /**/
Param pm;
{
static char hostnam[65];
static int got = 0;

	if (!got)
		{
		gethostname(hostnam,64);
		hostnam[64] = '\0';
		got = 1;
		}
	return hostnam;
}

char *ifsgetfn(pm) /**/
Param pm;
{
	return ifs;
}

void ifssetfn(pm,x) /**/
Param pm;char *x;
{
	if (x) { free(ifs); ifs = x; }
	inittyptab();
}

void histsizesetfn(pm,v) /**/
Param pm;long v;
{
	if ((histsiz = v) <= 2) histsiz = 2;
	resizehistents();
}

long histsizegetfn(pm) /**/
Param pm;
{
	return histsiz;
}

void lithistsizesetfn(pm,v) /**/
Param pm;long v;
{
	if ((lithistsiz = v) <= 2) lithistsiz = 2;
	resizehistents();
}

long lithistsizegetfn(pm) /**/
Param pm;
{
	return lithistsiz;
}

void mailchecksetfn(pm,x) /**/
Param pm;long x;
{
	mailcheck = (unsetflag) ? 600 : x;
}

void pathasetfn(pm,x) /**/
Param pm;char **x;
{
	freearray(path);
	path = x;
	newcmdnamtab();
}

char **pathagetfn(pm) /**/
Param pm;
{
	return path;
}

long errnogetfn(pm) /**/
Param pm;
{
	return errno;
}

char *dashgetfn(pm) /**/
Param pm;
{
static char buf[100];
char *val;
int t0;

	for (val = buf, t0 = ' ';t0 <= 'z'; t0++)
		if (isset(t0))
			*val++ = t0;
	*val = '\0';
	return buf;
}

void histcharssetfn(pm,x) /**/
Param pm;char *x;
{
	if (x) {
		bangchar = x[0];
		hatchar = (bangchar) ? x[1] : '\0';
		hashchar = (hatchar) ? x[2] : '\0';
		free(x);
	}
}

char *histcharsgetfn(pm) /**/
Param pm;
{
static char buf[4];

	buf[0] = bangchar;
	buf[1] = hatchar;
	buf[2] = hashchar;
	buf[3] = '\0';
	return buf;
}

char *homegetfn(pm) /**/
Param pm;
{
	return home;
}

void homesetfn(pm,x) /**/
Param pm;char *x;
{
	free(home);
	if (isset(CHASELINKS) && (home = xsymlink(x))) free(x);
	else home = x;
}

char *wordcharsgetfn(pm) /**/
Param pm;
{
	return wordchars;
}

void wordcharssetfn(pm,x) /**/
Param pm;char *x;
{
	free(wordchars);
	if (x) wordchars = x;
	else wordchars = ztrdup(DEFWORDCHARS);
	inittyptab();
}

char *underscoregetfn(pm) /**/
Param pm;
{
char *s,*t;

	if (!(s = qgetevent(curhist-1)))
		return "";
	for (t = s+strlen(s); t > s; t--)
		if (*t == HISTSPACE)
			break;
	if (t != s)
		t++;
	return t;
}

char *termgetfn(pm) /**/
Param pm;
{
	return term;
}

void termsetfn(pm,x) /**/
Param pm;char *x;
{
	if (term) free(term);
	term = x;
	if (!interact || unset(USEZLE))
		return;
	if (tgetent(termbuf,term) != 1)
		{
		zerr("can't find termcap info for %s",term,0);
		errflag = 0;
		termok = 0;
		}
	else
		{
		char tbuf[1024],*pp;
		int t0;

		termok = 1;
		for (t0 = 0; t0 != TC_COUNT; t0++)
			{
			pp = tbuf;
			if (tcstr[t0])
				free(tcstr[t0]);
			/* AIX tgetstr() ignores second argument */
			if (!(pp = tgetstr(tccapnams[t0],&pp)))
				tcstr[t0] = NULL, tclen[t0] = 0;
			else
				{
				tcstr[t0] = zalloc(tclen[t0] = strlen(pp)+1);
				memcpy(tcstr[t0],pp,tclen[t0]);
				}
			}

/* if there's no termcap entry for cursor left, use \b. */

		if (!tccan(TCLEFT))
			{
			tcstr[TCLEFT] = ztrdup("\b");
			tclen[TCLEFT] = 1;
			}

/* if there's no termcap entry for clear, use ^L. */

		if (!tccan(TCCLEARSCREEN))
			{
			tcstr[TCCLEARSCREEN] = ztrdup("\14");
			tclen[TCCLEARSCREEN] = 1;
			}

/* if the termcap entry for down is \n, don't use it. */

		if (tccan(TCDOWN) && tcstr[TCDOWN][0] == '\n')
			{
			tclen[TCDOWN] = 0;
			free(tcstr[TCDOWN]);
			tcstr[TCDOWN] = NULL;
			}

/* if there's no termcap entry for cursor up, forget it.
	Use single line mode. */

		if (!tccan(TCUP))
			termok = 0;
		}
}

void setparams() /**/
{
char **envp,**envp2,**envp3,*str;
char buf[50];
struct param *pm;
int ct;

	noerrs = 1;
	for (envp = environ, ct = 2; *envp; envp++,ct++);
	envp = environ;
	envp2 = envp3 = (char **) zalloc(sizeof(char *)*ct);
	for (; *envp; envp++)
		*envp2++ = ztrdup(*envp);
	*envp2 = NULL;
	envp = environ;
	environ = envp2 = envp3;
	for (; *envp; envp++,envp2++) {
		for (str = *envp; *str && *str != '='; str++);
		if (*str == '=') {
			char *iname = NULL;

			*str = '\0';
			pm = (isident(*envp) && !strchr(*envp, '[')) ?
				setsparam(iname = *envp,ztrdup(str+1)) : NULL;
			if (pm) {
				pm->flags |= PMFLAG_x;
				pm->env = *envp2;
				if (pm->flags & PMFLAG_SPECIAL)
					pm->env = replenv(pm->env,getsparam(iname));
			}
			*str = '=';
		}
	}
	pm = gethnode("HOME",paramtab);
	if (!(pm->flags & PMFLAG_x)) {
		pm->flags |= PMFLAG_x;
		pm->env = addenv("HOME",home);
	}
	pm = gethnode("PWD",paramtab);
	if (!(pm->flags & PMFLAG_x)) {
		pm->flags |= PMFLAG_x;
		pm->env = addenv("PWD",pwd);
	}
	pm = gethnode("LOGNAME",paramtab);
	if (!(pm->flags & PMFLAG_x)) {
		pm->flags |= PMFLAG_x;
		pm->env = addenv("LOGNAME",logname);
	}
	pm = gethnode("SHLVL",paramtab);
	if (!(pm->flags & PMFLAG_x))
		pm->flags |= PMFLAG_x;
	sprintf(buf,"%d",++shlvl);
	pm->env = addenv("SHLVL",buf);
	noerrs = 0;
}

char *mkenvstr(x,y) /**/
char *x;char *y;
{
char *z;
int xl = strlen(x),yl = strlen(y);

	z = zalloc(xl+yl+2);
	strcpy(z,x);
	z[xl] = '=';
	strcpy(z+xl+1,y);
	z[xl+yl+1] = '\0';
	return z;
}

void arrfixenv(s,t) /**/
char *s;char **t;
{
char **ep;
int sl = strlen(s);

	for (ep = environ; *ep; ep++)
		if (!strncmp(*ep,s,sl) && (*ep)[sl] == '=') {
			char *u = colonjoin(t);
			replenv(*ep,u);
			break;
		}
}

char *replenv(e,value) /**/
char *e;char *value;
{
char **ep;

	for (ep = environ; *ep; ep++)
		if (*ep == e)
			{
			char *s = e;

			while (*s++ != '=');
			*s = '\0';
			*ep = zalloc(strlen(e)+strlen(value)+2);
			strcpy(*ep,e);
			strcat(*ep,value);
			free(e);
			return *ep;
			}
	return NULL;
}

char *addenv(name,value) /**/
char *name;char *value;
{
char **ep,**ep2,**ep3;
int envct;

	for (ep = environ; *ep; ep++)
		{
		char *s = *ep,*t = name;

		while (*s && *s == *t) s++,t++;
		if (*s == '=' && !*t)
			{
			free(*ep);
			return *ep = mkenvstr(name,value);
			}
		}
	envct = arrlen(environ);
	ep = ep2 = (char **) zalloc((sizeof (char *))*(envct+3));
	for (ep3 = environ; *ep2 = *ep3; ep3++,ep2++);
	*ep2 = mkenvstr(name,value);
	ep2[1] = NULL;
	free(environ);
	environ = ep;
	return *ep2;
}

void delenv(x) /**/
char *x;
{
char **ep;

	ep = environ;
	for (; *ep; ep++)
		if (*ep == x)
			break;
	if (*ep)
		for (; ep[0] = ep[1]; ep++);
}

void convbase(s,v,base) /**/
char *s;long v;int base;
{
int digs = 0;
long x;

	if (base <= 1)
		base = 10;
	x = v;
	if (x < 0)
		{
		x = -x;
		digs++;
		}
	for (; x; digs++)
		x /= base;
	if (!digs)
		digs = 1;
	s[digs--] = '\0';
	x = (v < 0) ? -v : v;
	while (digs >= 0)
		{
		int dig = x%base;
		s[digs--] = (dig < 10) ? '0'+dig : dig-10+'A';
		x /= base;
		}
	if (v < 0)
		s[0] = '-';
}
