/*
 * Csh - a shell by PJAC
 * shvars.c - routines that deal with shell variables
 */

#include "csh.h"

extern	int	status;	/* execution status */

struct	ik {
	char	*s_name;
	int	s_iflag;
};

static	struct	ik	iknow[] = {
	"echo",		L_ECHO,
	"history",	L_HISTORY,
	"home",		L_HOME,
	"ignoreeof",	L_IGNOREEOF,
	"noglob",	L_NOGLOB,
	"path",		L_PATH,
	"prompt",	L_PROMPT,
	"shell",	L_SHELL,
	"status",	L_STATUS,
	"verbose",	L_VERBOSE,
	0, 0,
};

do_set(argc, argv)
char	**argv;
{
	struct	lvars	*lp, *xlp;
	char	lbuf[128];
	char	*p, *q, **xp;
	int	i;
	struct	ik	*ikp;
	int	ind = -1;
	char	*index();
	struct	lvars	*llp;

	if(argc <= 1){
		pr_set(lbuf);
		return(OK);
	}
	/*
	 * first parameter has a equal sign in it, split this parameter up
	 */
	if(p = index(argv[1], '=')){
		*p++ = 0;
		if(*p == 0){
			free(argv[0]);
			argv[0] = argv[1];
			argv[1] = strsave("=");
			argv--;
			++argc;
		}
	}
	if(argc != 2){
		/* set string to a null string */
		if(argc < 4 || strcmp(argv[2], "=") != 0)
			syn_err();
	}
	if(p = index(argv[1], '[')){
		if(argc == 2)
			syn_err();
		if(!(q = index(p, ']')) || q[1])
			syn_err();
		*p++ = 0;
		if(*p < '0' || *p > '9')
			syn_err();
		ind = atoi(p);
	}
	for(llp = 0, lp = Lvars ; lp ; lp = lp->L_next)
		if((i = strcmp(lp->L_name, argv[1])) == 0)
			break;
		else if(i < 0)
			llp = lp;
	if(lp == 0){
		if(ind != -1){
			puterr("Unknown variable.\n");
			return(ERR);
		}
		if(strcmp(argv[1], "status") == 0)
			return(OK);
		lp = (struct lvars *)mmalloc(sizeof(struct lvars));
		lp->L_next = 0;
		if(Lvars == 0)
			Lvars = lp;
		else {
			if(llp == 0){
				lp->L_next = Lvars;
				Lvars = lp;
			}
			else if(llp == Lvars){
				lp->L_next = Lvars->L_next;
				Lvars->L_next = lp;
			}
			else {
				lp->L_next = llp->L_next;
				llp->L_next = lp;
			}
		}
		lp->L_aval = 0;
		lp->L_name = strsave(argv[1]);
		lp->L_flags = 0;
		/*
		 * look up to see if we are setting some well known strings
		 */
		for(ikp = iknow; ikp->s_name ; ikp++)
			if(strcmp(lp->L_name, ikp->s_name) == 0)
				break;
		if(ikp->s_name)
			lp->L_flags = ikp->s_iflag;
	}
	if(lp->L_aval){
		if(ind != -1){
			for(i = 0, xp = lp->L_aval ; *xp ; xp++, i++);
			if(i < ind || ind == 0)
				goto memerr;
		}
		else {
			for(xp = lp->L_aval ; *xp ; xp++)
				free(*xp);
			free(lp->L_aval);
			lp->L_aval = 0;
		}
	}
	else if(ind != -1){
memerr:;
		puterr("Unknown variable member.\n");
		return(ERR);
	}

	if(argc > 4){
		if(ind != -1)
			syn_err();
		if(argc < 6 || *argv[3] != '(' || *argv[argc-1] != ')')
			syn_err();
		lp->L_aval = (char **)mmalloc(sizeof(char *)*(argc - 5 + 1));
		xp = argv + 4;
		for(i = 0 ; i < argc - 5 ; i++)
			lp->L_aval[i] = strsave(*xp++);
		lp->L_aval[i] = 0;
	}
	else if(argc == 2){	/* give it a null value */
		lp->L_aval = (char **)mmalloc(sizeof(char *));
		lp->L_aval[0] = 0;
	}
	else if(strcmp(argv[3], "(") == 0)	/* stop set x = ( */
		syn_err();
	else {
		if(ind != -1){
			ind--;
			free(lp->L_aval[ind]);
			lp->L_aval[ind] = strsave(argv[3]);
		}
		else {
			lp->L_aval = (char **)mmalloc(sizeof(char *)* 2);
			lp->L_aval[0] = strsave(argv[3]);
			lp->L_aval[1] = 0;
		}
	}

	if(lp->L_flags)
		set_svs(lp); /* set shell vars */
	return(OK);
}

/*
 * doesn't do assignment operators or inc/dec operators
 */

do_atsign(argc, argv)
char	**argv;
{
	char	lbuf[128];
	int	res;
	char	*av[5];

	if(argc == 1){
		pr_set(lbuf);
		return(OK);
	}
	if(argc < 4 || strcmp(argv[2], "="))
		syn_err();
	if(arg_eval(argc - 3, argv + 3, &res) == ERR)
		return(ERR);
	av[0] = 0;
	av[1] = argv[1];
	av[2] = argv[2];
	av[3] = my_Itoa(res);
	av[4] = 0;
	return(do_set(4, av));
}
	
set_path(xp)
char	**xp;
{
	char	tbuf[128];
	char	*p, *q;
	char	*av[4];

	for(p = tbuf ; (q = *xp++) ; ){
		while(*q)
			*p++ = *q++;
		if(!*xp)
			break;
#ifdef	MSDOS
		*p++ = ';';
#else
		*p++ = ':';
#endif
	}
	*p = 0;
	to_fslash(tbuf);		/* get all strings to be '/'ed */
	to_low(tbuf);
	if(Path)
		free(Path);
	Path = strsave(tbuf);
	clr_hash();	/* rebuild the hash tables */
	bld_hash();
	av[1] = "PATH";
	av[2] = tbuf;
	av[3] = 0;
	do_setenv(3, av);
}

pr_set(lbuf)
char	*lbuf;
{
	char	*p, **xp;
	struct	lvars	*lp;
	int	noval;
	int	seen_status = 0;
	static	char	xstatus[] = "status";

	for(lp = Lvars ; lp ; lp = lp->L_next){
		if(!seen_status && strcmp(lp->L_name, xstatus) >= 0){
			seen_status = 1;
			put_s("status = ");
			put_s(my_Itoa(status));
			put_s("\n");
		}
		noval = 0;
		switch(lp->L_flags & ~L_SYS){
		case L_ECHO:
			if(in_cshrc && !XVflag)
				continue;
			break;
		case L_VERBOSE:
			if(in_cshrc && !XVflag)
				continue;
			break;
		case L_IGNOREEOF:
		case L_NOGLOB:
			break;
		default:
			noval = 1;
			break;
		}
		put_s(lp->L_name);
		if(noval && lp->L_aval && lp->L_aval[0]){
			put_s(" = ");
			if(lp->L_aval[1])
				put_s("( ");
			for(xp = lp->L_aval ; p = *xp++ ;){
				put_s(p);
				if(*xp)
					put_s(" ");
			}
			if(lp->L_aval[1])
				put_s(" )");
		}
		put_s("\n");
	}
	if(!seen_status){
		put_s("status = ");
		put_s(my_Itoa(status));
		put_s("\n");
	}
}

do_prtenv(argc, argv)
char	**argv;
{
	char	**xp, *p, *q, *s;
	char	lbuf[128];

	for(xp = Real_Environ ; (p = *xp) ; xp++){
		q = index(p, '=');
		if(q == NULL)
			strcpy(lbuf, p);
		else {
			for(s = lbuf ; p < q ; *s++ = *p++);
			strcpy(s, " = ");
			strcat(s + 3, q+1);
		}
		put_s(lbuf);
		put_s("\n");
	}
}

do_unset(argc, argv)
char	**argv;
{
	argc--;
	argv++;
	while(*argv)
		do__unset(*argv++);
	return(OK);
}

do__unset(str)
char	*str;
{
	struct	lvars	*lp, *xlp;
	char	**xp;

	for(lp = Lvars ; lp ; lp = lp->L_next)
		if(strcmp(lp->L_name, str) == 0)
			break;
	if(!lp)			/* no string with that value */
		return;
	if(lp->L_flags & L_SYS)
		return;
	if(lp == Lvars)
		Lvars = lp->L_next;
	else {
		for(xlp = Lvars ; xlp->L_next != lp ; xlp = xlp->L_next);
		xlp->L_next = lp->L_next;
	}
	free(lp->L_name);
	if(lp->L_aval){
		for(xp = lp->L_aval ; *xp ; xp++)
			free(*xp);
		free( (char *)lp->L_aval);
	}
	switch(lp->L_flags){
	case L_ECHO:
		xflag = 0;
		break;
	case L_HISTORY:
		hist_set(0);
		break;
	case L_HOME:
		HOME[0] = 0;
		break;
	case L_IGNOREEOF:
		igneof = 0;
		break;
	case L_NOGLOB:
		noglob = 0;
		break;
	case L_PATH:
		if(Path)
			free(Path);
		Path = 0;
		break;
	case L_PROMPT:
		Prompt[0] = 0;
		break;
	case L_SHELL:
		SHELL[0] = 0;
		break;
	case L_VERBOSE:
		vflag = 0;
		break;
	}
	free( (char *)lp);
}

/*
 * called very early on to set up the argv variable,
 */

set_avl(name, val)
char	*name, *val;
{
	char	*av[1];

	av[0] = val;
	set_avs(name, val ? 1 : 0, av);
}

set_avs(name, argc, argv)
char	*name;
char	**argv;
{
	struct	lvars	*lp;
	struct	ik	*ikp;
	int	i;

	lp = (struct lvars *)mmalloc(sizeof(struct lvars));
	lp->L_next = Lvars;
	Lvars = lp;
	lp->L_name = strsave(name);
	lp->L_flags = 0;
	for(ikp = iknow; ikp->s_name ; ikp++)
		if(strcmp(lp->L_name, ikp->s_name) == 0)
			break;
	if(ikp->s_name)
		lp->L_flags = ikp->s_iflag;
	lp->L_aval = (char **)mmalloc(sizeof(char *)*(argc + 1));
	for(i = 0 ; i < argc ; i++)
		lp->L_aval[i] = strsave(*argv++);
	lp->L_aval[i] = 0;
	if(lp->L_flags)
		set_svs(lp);
}

set_svs(lp)
struct	lvars	*lp;
{
	char	*p;

	switch(lp->L_flags & ~L_SYS){
	case L_ECHO:
		xflag = 1;
		break;
	case L_HISTORY:
		p = lp->L_aval[0];
		if(!p || *p < '0' || *p > '9'){
			puterr("History needs a numeric value.\n");
			return(ERR);
		}
		hist_set(atoi(p));
		break;
	case L_HOME:
		if(!lp->L_aval[0])
			HOME[0] = 0;
		else
			strcpy(HOME, lp->L_aval[0]);
		to_fslash(HOME);
		to_low(HOME);
		break;
	case L_IGNOREEOF:
		igneof = 1;
		break;
	case L_NOGLOB:
		noglob = 1;
		break;
	case L_PATH:
		set_path(lp->L_aval);
		break;
	case L_PROMPT:
		Prompt[0] = lp->L_aval[0];
		break;
	case L_SHELL:
		if(!lp->L_aval[0])
			SHELL[0] = 0;
		else
			strcpy(SHELL, lp->L_aval[0]);
		to_fslash(SHELL);
		to_low(SHELL);
		break;
	/* case L_STATUS: never called
		break;
	 */
	case L_VERBOSE:
		vflag = 1;
		break;
	}
}

do_shift(argc, argv)
char	**argv;
{
	char	*p, **xp;
	struct	lvars	*lp;

	if(argc < 2)
		p = "argv";
	else
		p = argv[1];

	for(lp = Lvars ; lp ; lp = lp->L_next)
		if(strcmp(lp->L_name, p) == 0)
			break;
	if(!lp || !lp->L_aval || !lp->L_aval[0])
		return(OK);
	free(lp->L_aval[0]);
	for(xp = lp->L_aval ; *xp = xp[1] ; xp++); /* do the shift */
	return(OK);
}
