/*
 * Csh - shell by PJAC
 * hist.c - history handling routines
 */

#include "csh.h"

extern	char	hist_expand;
/*
 * called from readin - do history expansion
 */

get_hist(mmask)
{
	int	c;
	int	i;
	char	*p, *q;
	int	sstart;
	int	scar;
	char	tbuf[128];

	switch( c = in_1char()){
	case '!':		/* !! last command */
		if((p = last_cmd) != NULL){
			c = in_1char();
			break;
		}
		return(OK);
	case '*':
		if((p = last_cmd) == NULL)
			return(OK);
		if(!(p = skipsp(p))){
			add_char('!' | mmask);
			add_char('*' | mmask);
			return(OK);
		}
		if(next_object(tbuf, &p, (char *)0, TRUE) == COMMENT)
			p = "";
		c = in_1char();
		break;
	case '0':
	case '1':
	case '2':
	case '3':
	case '4':
	case '5':
	case '6':
	case '7':
	case '8':
	case '9':
	case '-':
		/* !2 a numeric command */
		p = tbuf;
		*p++ = c;
		while( (c = in_1char()) != EOF)
			if(c < '0' || c > '9')
				break;
			else
				*p++ = c;
		*p = 0;
		i = atoi(tbuf);
		if(i < 0)
			i = Curcnum - i;
		p = 0;
		/* is it in range */
		if(i < Curcnum && i >= Curcnum - Hist_siz)
			p = History[i % Hist_siz];
		if(p)
			break;
		add_char('!' | mmask);
		p = tbuf;
		while(*p)
			add_char(*p++ | mmask);
		if(c == EOF)
			return(EOF);
		Cur_ins->i_nleft++;	/* push back last char */
		Cur_ins->i_ptr--;
		return(OK);
	case '?':
		p = tbuf;
		while((c = in_1char()) != EOF){
			if(index(IFS, c))
				break;
			if(c == '?'){
				c = in_1char();
				break;
			}
			*p++ = c;
		}
		*p = 0;
		sstart = strlen(tbuf);
		if(sstart != 0)
			for(i = Hist_siz ; i >= 0 ; i--){
				p = History[(Curcnum + i -1) % Hist_siz];
				if(p == 0)
					continue;
				scar = strlen(p);
				for(q = p; q <= p + scar - sstart ; q++)
					if(strncmp(q, tbuf, sstart) == 0)
						goto found;
		}
		add_char('!' | mmask);
		p = tbuf;
		while(*p)
			add_char(*p++ | mmask);
		if(c == EOF)
			return(EOF);
		Cur_ins->i_nleft++;	/* push back last char */
		Cur_ins->i_ptr--;
		return(OK);
	case EOF:
		return(EOF);
	default:
		p = tbuf;
		do {
			if(index(IFS, c))
				break;
			*p++ = c;
		} while((c = in_1char()) != EOF);
		*p = 0;
		sstart = strlen(tbuf);
		if(sstart == 0)
			i = -1;
		else for(i = Hist_siz ; i >= 0 ; i--){
			p = History[(Curcnum + i -1) % Hist_siz];
			if(p && strncmp(p, tbuf, sstart) == 0)
				goto found;
		}
		add_char('!' | mmask);
		p = tbuf;
		while(*p)
			add_char(*p++ | mmask);
		if(c == EOF)
			return(EOF);
		Cur_ins->i_nleft++;	/* push back last char */
		Cur_ins->i_ptr--;
		return(OK);
	}
found:;
	/*
	 * get here if found a valid string to put into the output
	 * now check for editing chars
	 */
	if(c == EOF)
		return(EOF);
	hist_expand = TRUE;
	if(!index(":^$*-%", c)){
		Cur_ins->i_nleft++;
		Cur_ins->i_ptr--;
		while(*p)
			add_char(*p++ | mmask);
		return(OK);
	}
	if(c == ':'){
		c = in_1char();
		if(c == EOF)
			return(EOF);
	}
	/*
	 * char is a ':' operator
	 * now find out what to do with it, p is the string that we have
	 * from the history
	 */
	p = skipsp(p);
	if(!p)
		return(ERR);
	switch(c){
	case '^':
		if(next_object(tbuf, &p, (char *)0, TRUE) == COMMENT)
			return(OK);
		if(!(p = skipsp(p)))
			return(OK);
		if(next_object(tbuf, &p, (char *)0, TRUE) == COMMENT)
			return(OK);
		p = tbuf;
		break;
	case '$':
		do {
			if(next_object(tbuf, &p, (char *)0, TRUE) == COMMENT)
				return(OK);
		} while(p = skipsp(p));
		p = tbuf;
		break;
	case '*':
		if(next_object(tbuf, &p, (char *)0, TRUE) == COMMENT)
			return(OK);
		if(!(p = skipsp(p)))
			return(OK);
		break;
	case '-':
		break;
	case '%':
		break;
	case '0':
		if(next_object(tbuf, &p, (char *)0, TRUE) == COMMENT)
			return(OK);
		p = tbuf;
		break;
	default:
		if(c < '1' || c > '9'){
			Cur_ins->i_nleft++;
			Cur_ins->i_ptr--;
			break;
		}
		
	}
	for(c = in_1char(); c == ':'; c = in_1char()){
		switch(c = in_1char()){
		case EOF:
			return(EOF);
		case 'h':
			break;
		}
	}
	if(c == EOF)
		return(EOF);
	while(*p)
		add_char(*p++ | mmask);
	return(OK);
}

/*
 * ^fred^frog^ processing
 */

get_bhist(mmask)
{
	char	match[128];
	char	repl[128];
	char	*p, *q;
	int	c;
	int	mlen, rlen, slen;

	for(p = match ; (c = in_1char()) != EOF ; *p++ =c)
		if(c == '^')
			break;
	if(c == EOF)
		return(EOF);
	*p = 0;
	for(p = repl ; (c = in_1char()) != EOF ; *p++ =c){
		if(c == '^')
			break;
		if(index(IFS, c))
			break;
	}
	if(c == EOF)
		return(EOF);
	*p = 0;
	if(c != '^'){
		Cur_ins->i_nleft++;
		Cur_ins->i_ptr--;
	}
	if(last_cmd == 0)
		return(OK);
	slen = strlen(last_cmd);
	if((mlen = strlen(match)) == 0)
		return(OK);
	for(q = last_cmd ; q <= last_cmd + slen - mlen ; q++)
		if(strncmp(q, match, mlen) == 0){
			for(p = last_cmd ; p < q ; p++)
				add_char(*p | mmask);
			for(p = repl ; *p ; p++)
				add_char(*p | mmask);
			for(p = q + mlen ; *p ; p++)
				add_char(*p | mmask);
			break;
		}
	return(OK);
}

/*
 * add the current command to the history list
 */

add_to_hist()
{
	char	**p;

	if(Cur_ins->i_fblk)	/* if in loops don't do loops */
		return;
	if(Hist_siz < 0){
		if(last_cmd != NULL)
			free(last_cmd);
		last_cmd = strsave(skipsp(in_buf));
		Curcnum++;
		return;
	}
	if( *(p = &History[Curcnum % Hist_siz]) != NULL)
		free(*p);
	last_cmd = *p = strsave(skipsp(in_buf));
	Curcnum++;
}

/*
 * print out the history list
 */

do_hist(argc, argv)
char	**argv;
{
	int	i, j, k;
	char	*p, *q;
	char	*targs[5];
	char	tbuf[10];

	if(argc != 1){
		targs[0] = "set";
		targs[1] = "history";
		targs[2] = "=";
		targs[3] = argv[1];
		targs[4] = 0;
		return(do_set(4, targs));
	}
	if(Hist_siz < 0)
		return(OK);
	i = Curcnum;
	for(j = 0 ; j < Hist_siz ; j++, i++){
		p = History[i % Hist_siz];
		if(p != 0){
			q = my_Itoa(i - Hist_siz);
			for(k = 6 - strlen(q) ; k >= 0 ; k--)
				tbuf[k] = ' ';
			strcpy(tbuf + 6 - strlen(q), q);
			put_s(tbuf);
			put_s("  ");
			put_s(p);
			put_s("\n");
		}
	}
	return(OK);
}

/*
 * set the history length
 */

hist_set(val)
{
	int	i;
	char	**xp;
	char	**ohist;

	if(val < 0){
		puterr("Cannot have negative history list.\n");
		return(ERR);
	}
	if(val == Hist_siz)	/* doesn't change history size - ok */
		return(OK);
	if(History != 0){	/* delete old history list */
		for(xp = History, i = 0 ; i < Hist_siz ; i++, xp++)
			if(*xp)
				free(*xp);
		free( (char *)History);
		History = 0;
	}
	Hist_siz = -1;
	if(val == 0)		/* want's no history */
		return(OK);
	History = (char **)mmalloc(sizeof(char *)*val);
	for(xp = History, i = 0; i < val ; i++, *xp++ = 0);
	Hist_siz = val;
	History[Curcnum % Hist_siz] = last_cmd;
	return(OK);
}
